Railway Operation Simulator  v2.13.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand() & random()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 //#include "DisplayUnit.h" included in TrackUnit.h
49 #include "PerfLogUnit.h"
50 #include "Utilities.h"
51 
52 // ---------------------------------------------------------------------------
53 #pragma package(smart_init)
54 
56 
57 // ---------------------------------------------------------------------------
58 
59 int TTrain::NextTrainID = 0; // has to be initialised outside the class
60 
61 // ---------------------------------------------------------------------------
62 
63 TExitInfo::TExitInfo() //default constructor
64 {
65  ServiceReference = " ";
66  RepeatNumber = 0;
67  TimeToExitSecs = -1;
68 }
69 
70 // ---------------------------------------------------------------------------
71 
72 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
73  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
74  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
75  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
76  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
77  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
78 /*
79  Construct a new train with general default values and input values for position and headcode.
80  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
81  This is because trains are kept in a vector and vectors erase elements during internal operations.
82  Deletion is explicit by using a special function. Increment the static class member NextTrainID
83  after setting this train's ID.
84 */
85 
86 {
87  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
88  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
89  AnsiString(TrainModeIn));
90  // AutoControl = true;//all trains start in auto control
91  UpdateCounter = 0;
92  TimeTimeLocArrived = false;
93  Derailed = false;
94  DerailPending = false;
95  Crashed = false;
96  StoppedAtBuffers = false;
97  StoppedAtSignal = false;
98  StoppedAtLocation = false;
99  StoppedAfterSPAD = false;
100  StoppedWithoutPower = false; // new at v2.4.0
101  StoppedForTrainInFront = false;
102  SignallerStoppingFlag = false;
103  SignallerStopped = false;
104  SignallerRemoved = false;
105  NotInService = false;
106  HoldAtLocationInTTMode = false;
107  AllowedToPassRedSignal = false;
108  CallingOnFlag = false;
109  BeingCalledOn = false;
110  DepartureTimeSet = false;
112  TimetableFinished = false;
113  LastActionDelayFlag = false;
114  OneLengthAccelDecel = false;
115  TrainCrashedInto = -1;
117  Plotted = false;
118  TrainGone = false;
119  SPADFlag = false;
120  FrontCodePtr = new Graphics::TBitmap;
121  FrontCodePtr->PixelFormat = pf8bit;
122  FrontCodePtr->Height = 8;
123  FrontCodePtr->Width = 8;
125  FrontCodePtr->Transparent = false;
126  AValue = sqrt(2 * PowerAtRail / Mass);
128  TerminatedMessageSent = false;
129  JoinedOtherTrainFlag = false;
131  FollowOnServiceRef = ""; //added at v2.12.0
132  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
133  StepForwardFlag = false;
135  for(int x = 0; x < 4; x++)
136  {
137  HeadCodeGrPtr[x] = new Graphics::TBitmap;
138  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
139  HeadCodeGrPtr[x]->Height = 8;
140  HeadCodeGrPtr[x]->Width = 8;
142  HeadCodeGrPtr[x]->Transparent = false;
143  }
144  for(int x = 0; x < 4; x++)
145  {
146  BackgroundPtr[x] = new Graphics::TBitmap;
147  BackgroundPtr[x]->PixelFormat = pf8bit;
148  BackgroundPtr[x]->Height = 8;
149  BackgroundPtr[x]->Width = 8;
151  BackgroundPtr[x]->Transparent = false;
152  }
153  for(int x = 0; x < 4; x++)
154  {
156  // set here to ensure have values
157  }
158  for(int x = 0; x < 4; x++)
159  {
160  PlotElement[x] = -1; // marker for not plotted yet
161  }
162  for(int x = 0; x < 3; x++)
163  {
164  OldZoomOutElement[x] = -1; // marker for not plotted yet
165  }
167  NextTrainID++;
168 
169  // new values added to complete initialisation of all TTrain variables
170 
171  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
172  // TrainDataEntryPtr, initialise in AddTrain
174  FrontElementLength = 0;
175  EntrySpeed = 0;
176  ExitSpeedHalf = 0;
177  ExitSpeedFull = 0;
178  MaxExitSpeed = 0;
179  BrakeRate = 0;
181  FirstHalfMove = true;
182  EntryTime = 0;
183  ExitTimeHalf = 0;
184  ExitTimeFull = 0;
185  ReleaseTime = 0;
186  TRSTime = 0;
187  LastActionTime = 0;
188  Straddle = MidLag;
189  LeadElement = -1;
190  LeadEntryPos = 0;
191  LeadExitPos = 0;
192  MidElement = -1;
193  MidEntryPos = 0;
194  MidExitPos = 0;
195  LagElement = -1;
196  LagEntryPos = 0;
197  LagExitPos = 0;
198  TrainFailed = false; // added at v2.4.0
199  for(int x = 0; x < 4; x++)
200  {
201  HOffset[x] = 0;
202  VOffset[x] = 0;
203  PlotEntryPos[x] = 0;
204  }
205  OpTimeToAct = 60; // default value, new at v2.2.0
206  TimeToExit = -1;
207  ExitPair.first = -1;
208  ExitPair.second = -1;
209  MinsDelayed = 0.0; // new at v2.2.0
210  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
211  FinishJoinLogSent = false;
212  // added at v2.4.0 to prevent repeatdly logging the event
215  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
219  ZeroPowerNoCDTMessage = false;
224  TrainFailurePending = false;
225  SkippedDeparture = false;
226  ActionsSkippedFlag = false;
227  SkipPtrValue = 0;
228  TrainSkippedEvents = 0;
229  DelayedRandMins = 0; //added at v2.13.0
230  NewDelay = 0; //added at v2.13.0
231  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
232  ActualArrivalTime = TDateTime(0); //added at v2.13.0
233  LastSigPassedFailed = false; //added at v2.13.0
234  Utilities->CallLogPop(648);
235 }
236 
237 // ---------------------------------------------------------------------------
238 
239 void TTrain::DeleteTrain(int Caller)
240 /*
241  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
242  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
243  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
244  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
245  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
246  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
247  No need to delete HeadCodePosition as that just points to existing bitmaps
248 */{
249  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
250  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
251  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
252  if(Display->ZoomOutFlag)
253  {
255  }
256  if(FrontCodePtr == 0)
257  {
258  throw Exception("Error in attempting to delete FrontCodePtr");
259  }
260  delete FrontCodePtr;
261  FrontCodePtr = 0;
262  for(int x = 0; x < 4; x++)
263  {
264  if(BackgroundPtr[x] == 0)
265  {
266  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
267  }
268  delete BackgroundPtr[x];
269  BackgroundPtr[x] = 0;
270  }
271  for(int x = 0; x < 4; x++)
272  {
273  if(HeadCodeGrPtr[x] == 0)
274  {
275  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
276  }
277  delete HeadCodeGrPtr[x];
278  HeadCodeGrPtr[x] = 0;
279  }
280  Utilities->CallLogPop(649);
281 }
282 
283 // ---------------------------------------------------------------------------
284 
286 /*
287  Plots the train starting position on screen. Note that the check for starting on straight points &
288  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
289  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
290  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
291  Set the headcode graphics pointers from the headcode text, then check whether starting at a
292  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
293  for the continuation element. Otherwise set Lead and Mid values,
294 
295  and Lead element value unless
296  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
297  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
298  then check if a train on either Mid or Lag and if so give a warning message and return false so
299  that the calling function can delete the train. Plot the Mid element train values then do similarly
300  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
301  the train. Finally set the Plotted flag and return true.
302 */{
303  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
304  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
305 
307  // PlotStartTime = TrainController->TTClockTime;
308  FirstHalfMove = true;
309 
310  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
311  // 'claim' it for this train to prevent any other waiting trains trying to enter
313  {
314  LagElement = -1; // not to be plotted
315  LagExitPos = 0; // not to be plotted
316  LagEntryPos = 0; // not to be plotted
317  MidElement = -1; // not to be plotted
318  MidExitPos = 0; // not to be plotted
319  MidEntryPos = 0; // not to be plotted
321  LeadExitPos = 1; // will be 1 for continuation entry
322  LeadEntryPos = 0;
323 
325  MaxExitSpeed = StartSpeed; // initial value
327  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
328  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
329  if(EntrySpeed > SpeedLimit)
330  {
331  EntrySpeed = SpeedLimit;
332  }
334  {
336  }
338  // LeadElement is the element to be entered
339 
340  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
341  // can achieve ExitSpeedFull at the half braking rate.
343  {
344  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
345  if(TempEntrySpeed < EntrySpeed)
346  {
347  EntrySpeed = TempEntrySpeed;
349  }
350  }
351  Straddle = MidLag; // only for starting on a continuation
353  // no need to stop gap flashing if start on continuation
354  }
355  else // not starting at a continuation
356  {
357  LagElement = -1;
358  LagEntryPos = 0;
359  LagExitPos = 0;
366 
368  MaxExitSpeed = StartSpeed; // initial value
370  bool TempDerail = false; // dummy
371  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
373  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
374  {
375  StoppedWithoutPower = true;
376  }
377  // facing buffers check - ignore starting speed if start facing buffers
378  StoppedAtBuffers = false;
379  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
382  {
383  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
384  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
385  EntrySpeed = 0;
386  ExitSpeedHalf = 0;
387  ExitSpeedFull = 0;
388  MaxExitSpeed = 0;
389  // SetTrainMovementValues not called so set this here
390  BrakeRate = 0;
393  StoppedAtSignal = false;
394  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
395  // signal check is an 'else'
396  if(!StoppedAtLocation)
397  {
398  StoppedAtBuffers = true; // stopped at location takes precedence
399  }
400  }
401 
402  // facing continuation check - don't allow to stop even if no power
404  {
405  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
406  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
410  BrakeRate = 0;
411  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
412  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
413  }
414 
415  // Signal check
416  else if((NextElementPosition > -1) && (NextEntryPos > -1))
417  // condition check added as precaution after SloughIECC error reported by James U
418  {
419  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
420  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
421  {
422  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
423  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
424  EntrySpeed = 0;
425  ExitSpeedHalf = 0;
426  ExitSpeedFull = 0;
427  MaxExitSpeed = 0;
428  BrakeRate = 0;
431  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
432  {
433  StoppedAtSignal = true;
435  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
436  }
438  {
439  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
440  // forwards, but don't change the background colour so still shows as stopped at location
441  StoppedAtSignal = true;
442  }
443  }
444  else
445  {
446  StoppedAtSignal = false;
447  if(NextEntryPos > 1)
448  {
449  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
450  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
451  }
452  else
453  {
454  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
455  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
456  }
457  if(EntrySpeed > SpeedLimit)
458  {
459  EntrySpeed = SpeedLimit;
460  }
462  {
464  }
466  TDateTime TestTime = TrainController->TTClockTime; // test
467  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
468  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
469  // NextElement is the element to be entered
470 
471  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
472  // can achieve ExitSpeedFull at the half braking rate.
474  {
475  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
476  // half braking
477  if(TempEntrySpeed < EntrySpeed)
478  {
479  EntrySpeed = TempEntrySpeed;
480  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
481  }
482  }
483  }
484  }
486  {
487  throw Exception("Error, LeadElement Exit Connection is NotSet");
488  }
489  }
490  if(MidElement > -1) // will be -1 if start on continuation
491  {
492  Straddle = LeadMid;
496  {
497  for(int x = 0; x < 4; x++)
498  {
499  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
500  }
501  }
502  else
503  {
504  for(int x = 0; x < 4; x++)
505  {
507  }
508  }
509  if(TrainMode == Timetable)
510  {
512  }
513  else
514  {
516  }
518  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
519 
522 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
523  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
524  {
525  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
526  Utilities->CallLogPop(651);
527  return false;
528  }
529 */
534  PlotTrainGraphic(8, 0, Display);
535  PlotTrainGraphic(9, 1, Display);
536 
539 
540  // pick up background bitmaps [2] & [3]
541 
544 
545  PlotElement[2] = MidElement;
547  PlotElement[3] = MidElement;
549  PlotTrainGraphic(10, 2, Display);
550  PlotTrainGraphic(11, 3, Display);
551  // Plotted = true; set in PlotTrainGraphic
552  }
553  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
554  Utilities->CallLogPop(652);
555 }
556 
557 // ---------------------------------------------------------------------------
558 void TTrain::UnplotTrain(int Caller)
559 {
560  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
561  if(!Plotted)
562  {
563  return;
564  }
565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
566 
567  if(Straddle == MidLag)
568  {
569  if(MidElement > -1)
570  {
575  // to force plot of locked route marker, needed once only for the element
576  }
577  if(LagElement > -1)
578  {
583  // to force plot of locked route marker, needed once only for the element
584  }
585  }
586  else if(Straddle == LeadMidLag)
587  {
588  if(LeadElement > -1)
589  {
592  // to force plot of locked route marker, needed once only for the element
593  }
594  if(MidElement > -1)
595  {
600  // to force plot of locked route marker, needed once only for the element
601  }
602  if(LagElement > -1)
603  {
606  // to force plot of locked route marker, needed once only for the element
607  }
608  }
609  else if(Straddle == LeadMid)
610  {
611  if(LeadElement > -1)
612  {
617  // to force plot of locked route marker, needed once only for the element
618  }
619  if(MidElement > -1)
620  {
625  // to force plot of locked route marker, needed once only for the element
626  }
627  }
628  if(LeadElement > -1)
629  {
631  }
632  if(MidElement > -1)
633  {
635  }
636  if(LagElement > -1)
637  {
639  }
640  Plotted = false;
642  Display->Update();
643  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
644  // resurrected when Update() dropped from PlotOutput etc
645  Utilities->CallLogPop(653);
646 }
647 
648 // ----------------------------------------------------------------------------
649 
650 void TTrain::UpdateTrain(int Caller)
651 /*
652  Note: Some changes made since comments written
653 
654  Brief:
655  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
656  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
657  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
658  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
659  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
660  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
661  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
662  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
663  changed to MidLag within the function and all elements moved down one, old Mid becomes
664  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
665  incremented to reflect the position the train now occupies.
666 
667  Detail:
668  Set TrainFailurePending if all conditions met
669  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
670  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
671  and return.
672  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
673  If there's a LagElement (there will be but include check for good practice - next
674  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
675  train fully on offending point - Derail set and DerailPanding reset, train background
676  colour changed (note that BackgroundColour is a property of the train itself) then return.
677  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
678  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
679  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
680  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
681  if LeadElement is a fouled trailing point.
682  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
683  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
684  replotting the last background segment and checking whether the element is a bridge or crossover with the other
685  track in a route, in which case the route colour is replotted.
686  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
687  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
688  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
689  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
690  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
691  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
692  train can be deleted by the calling function, and the function returns.
693  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
694  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
695  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
696  basic red aspect.
697 
698  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
699  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
700  regardless of direction, and with the correct front code colour.
701 
702  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
703  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
704  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
705  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
706  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
707  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
708  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
709 
710  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
711  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
712  changed similarly. The function then returns.
713 
714  If Crashed is not set then Straddle is incremented and the function returns.
715 */
716 
717 {
718  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
719  UpdateCounter++;
720  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
721  if(UpdateCounter >= 100)
722  {
723  UpdateCounter = 0;
724  }
725  int RandRange = (TrainController->MTBFHours * 3600) / 53;
726 
727  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
728  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
729  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
730  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
731  // RandomFailureCounter value is fixed for a full cycle of train updates so this
732  // makes sure there's no bunching of failures as there is for a fixed comparison number
733  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
734  // gives a random number between 0 and 16384 (defined as RAND_MAX in stdlib.h)
735  {
736  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
737  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
738  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
739  // don't fail if:
740  // (a) on a continuation (entering or leaving);
741  // (b) already failed;
742  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
743  // (d) train terminated;
744  // (e) crashed or derailed; or
745  // (f) under signaller control and stopped.
746  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
747  {
748  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
749  // max value for RandRange is over 2x10^9
750  {
751  // here if failure due
752  TrainFailurePending = true;
753  // fail when PlotElements set to proper Lead & Mid Elements
754  }
755  }
756  }
757 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
758  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
759  {
760  StoppedWithoutPower = true;
761  }
762 */
763 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
764 // THVShortPair ExitPair; //added at v2.10.0
765  int LockedVectorNumber;
766  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
767  // default values - these needed for route checker below
768  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
769 
771  {
773  }
774  if(Crashed || Derailed)
775  {
777  {
778  PlotTrain(7, Display);
779  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
780  Display->Update();
781  }
782  OpTimeToAct = 0.0;
783  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
784  Utilities->CallLogPop(1017);
785  return; // no further action, user has to remove or work around
786  }
788  {
790  }
792  {
794  }
796  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
797  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
798  // to move & then stop again at the same station
799  {
800  TimeTimeLocArrived = false;
801  }
802  if(!Stopped() && !SPADFlag && !TrainFailed)
803  {
805  }
806  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
807  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
808 /* old version where force a stop at buffers regardless of speed
809  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
810  else StoppedAtBuffers = false;
811 */
812 
813  // new version where crash if run into buffers
814  if(!Crashed)
815  {
816  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
817  {
818  if(ExitSpeedFull > 1)
819  {
820  Crashed = true;
824  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
825  // no need for missed action logs - will be sent when train removed
826  StoppedAtBuffers = false;
827  }
829  // stopped at location & stopped without power take precedence
830  {
831  StoppedAtBuffers = true;
832  }
833  else
834  {
835  StoppedAtBuffers = false;
836  }
837  }
838  else
839  {
840  StoppedAtBuffers = false;
841  }
842  }
843  else
844  {
845  StoppedAtBuffers = false;
846  }
847  // if crashed don't want stopped at buffers set
848 
849  // also crash if run into a level crossing that is changing or has barriers up
850  if(!Crashed)
851  {
852  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
853  {
854  int H = Track->TrackElementAt(873, LeadElement).HLoc;
855  int V = Track->TrackElementAt(874, LeadElement).VLoc;
856  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
857  {
858  Crashed = true;
862  // no need for missed action logs - will be sent when train removed
863  }
864  }
865  }
867  {
869  }
870  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
872  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
873  {
874  HoldAtLocationInTTMode = true;
875  }
876  else if(TrainMode == Timetable)
877  {
878  HoldAtLocationInTTMode = false;
879  }
880  // in Signaller mode HoldAtLocationInTTMode not changed
881 
882  // check if departure pending & set times unless already set
883  if(TrainMode == Timetable)
884  {
886  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
887  {
888  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
889  {
890  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
891  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
892  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
893  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
894  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
895  "of a shortage of on train crew"};
896  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
897  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
898  //at start or have no departure time set.
899  NewDelay = 0; //section relating to random delays added at v2.13.0
900  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
901  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
902  if(DwellTime < TDateTime(30.0 / 86400))
903  {
904  DwellTime = TDateTime(30.0 / 86400);
905  }
906  int randval = random(10000);
907  if(randval != 0) //if randval == 0 or DelayMode == Nil then NewDelay will be 0 as set above
908  {
909  if(Utilities->DelayMode == Minor)
910  {
911  if(randval < Utilities->MinorDelayCutoff)
912  {
913  NewDelay = Utilities->MinorDelayFactor * log(Utilities->MinorDelayCutoff/randval);//minutes (confusingly log in C++Builder gives the natural logarithm)
914  }
915  }
916  else if(Utilities->DelayMode == Moderate)
917  {
918  if(randval < Utilities->ModerateDelayCutoff)
919  {
921  }
922  }
923  else if(Utilities->DelayMode == Major)
924  {
925  if(randval < Utilities->MajorDelayCutoff)
926  {
928  }
929  }
930  }
931 //NewDelay = 25; //test
932  if(double(TrainController->TTClockTime) <= (Utilities->LastDelayTTClockTime + 5.0/1440.0)) //if within 5 mins of last delay for any train
933  { //then don't delay. Added at v2.13.0
934  NewDelay = 0;
935  }
936  if(NewDelay < 1)
937  {
938  NewDelay = 0;
939  }
940  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
941  {
942  NewDelay = 0;
943  }
944  else
945  {
946  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
947  }
948  if(DelayedRandMins > 0)
949  {
950  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
951  }
952  if(DelayedRandMins < 0)
953  {
954  DelayedRandMins = 0;//can't be less than zero
955  }
957  {
958  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
959  //the formula above already includes knock-on effects
960  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
961 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
962  }
963  else
964  {
965  NewDelay = 0;
966 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
967  }
968  ReleaseTime = LastActionTime + TDateTime(NewDelay / 1440); //earliest possible release time
969  if(NewDelay < 0.5) //less than the 30 secs min interval
970  {
971  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
972  }
973  if(ReleaseTime < TimetableReleaseTime)
974  {
975  ReleaseTime = TimetableReleaseTime; //back to correct time
976  NewDelay = 0;
977  DelayedRandMins = 0;
978  }
979  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
980  {
981  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
982  }
983 
984  if(DelayedRandMins < NewDelay) //may be if reduced above, but if so need to reduce NewDelay also
985  {
987  }
988  //may be possible to simplify all the above but as it seems to work ok leave as is
989  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
990  {
992  if(int(NewDelay) == 1)
993  {
995  ActionVectorEntryPtr->LocationName + " by 1 minute");
996  PerfLogForm->PerformanceLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: " + HeadCode + " delayed at " +
997  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
998  }
999  else
1000  {
1001  Display->WarningLog(11, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1002  ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) + " minutes");
1003  if(NewDelay >= 10) //give variable reasons for >= 10 mins
1004  {
1005  int randval2 = rand() % 24; //24 reasons
1006  AnsiString Reason = ReasonArray[randval2];
1008  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1009  " minutes because " + Reason);
1010  }
1011  else
1012  {
1014  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1015  " minutes because of a minor problem");
1016  }
1017  }
1018  }
1019  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1020  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1021  DepartureTimeSet = true;
1022  }
1023  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1024  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1025  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1026  //will run without any random delays which might cause additional complications from mixing modifications and best avoided.
1027  NewDelay = 0;
1028  DelayedRandMins = 0;
1030  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1031  {
1032  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1033  }
1034  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1035  DepartureTimeSet = true;
1036  }
1037  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1038  {//for when skip to a new service at a pass location. As above this also avoids any random delays, and will avoid above segment because
1039  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1040  //from mixing modifications so best avoided.
1041  NewDelay = 0;
1042  DelayedRandMins = 0;
1044  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1045  {
1046  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1047  }
1048  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1049  DepartureTimeSet = true;
1050  }
1051  }
1052  }
1053  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1054  {
1055  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1056 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1057 // this->ExitPair = ExitPair;
1058  // calculate every 1 sec (in real time, not timetable time) for all trains
1059  }
1060  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1061  if(TrainMode == Timetable)
1062  {
1063  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1064  {
1065  RemainHereLogNotSent = true;
1066  }
1068  {
1069  // ignore TimeLoc & TTLoc departures
1070  // Action logs given in functions
1072  LastActionTime + TDateTime(30.0 / 86400)))
1073  {
1074  if(ActionVectorEntryPtr->Command == "fsp")
1075  {
1076  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1077  FrontTrainSplit(0);
1078  if(TrainFailurePending) // ok, stopped so PlotElements set
1079  {
1080  TrainHasFailed(0);
1081  }
1082  Utilities->CallLogPop(2041);
1083  return;
1084  }
1085  else if(ActionVectorEntryPtr->Command == "rsp")
1086  {
1087  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1088  RearTrainSplit(0);
1089  if(TrainFailurePending) // ok, stopped so PlotElements set
1090  {
1091  TrainHasFailed(1);
1092  }
1093  Utilities->CallLogPop(2042);
1094  return;
1095  }
1096  else if(ActionVectorEntryPtr->Command == "Fjo")
1097  {
1098  FinishJoin(0);
1099  }
1100  else if(ActionVectorEntryPtr->Command == "jbo")
1101  {
1102  JoinedBy(0);
1103  }
1104  else if(ActionVectorEntryPtr->Command == "cdt")
1105  {
1106  ChangeTrainDirection(0, false);
1107  }
1108  else if(ActionVectorEntryPtr->Command == "Fns")
1109  {
1110  NewTrainService(0, false);
1111  }
1112  else if(ActionVectorEntryPtr->Command == "Frh")
1113  {
1114  RemainHere(0);
1115  }
1116  else if(ActionVectorEntryPtr->Command == "Fer")
1117  {
1118  TimetableFinished = true;
1119  }
1120  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1121  else if(ActionVectorEntryPtr->Command == "F-nshs")
1122  {
1124  }
1125  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1126  {
1127  RepeatShuttleOrRemainHere(0, false);
1128  }
1129  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1130  {
1132  }
1133 /*
1134  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1135  shuttle headcode (no train creation)
1136  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1137  remain here
1138  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1139  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1140 */
1141  }
1142  }
1143  else
1144  {
1146  {
1148  }
1149  }
1150  }
1151  if(TrainMode == Timetable)
1152  {
1153  if(StoppedAtBuffers)
1154  {
1155  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1156  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1157  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1158  if(BufferLocation == "")
1159  {
1161  }
1162  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1163  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1164  {
1168  {
1170  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1171  // Drop missed actions so user can still use sig mode to get back on track
1173  }
1174  if(TrainFailurePending) // ok, stopped so PlotElements set
1175  {
1177  TrainHasFailed(2);
1178  }
1179  Utilities->CallLogPop(1020);
1180  return;
1181  }
1182  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1183  ReleaseTime))
1184  {
1187  {
1190  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1191  // Drop missed actions so user can still use sig mode to get back on track
1193  }
1194  if(TrainFailurePending) // ok, stopped so PlotElements set
1195  {
1197  TrainHasFailed(3);
1198  }
1199  Utilities->CallLogPop(1397);
1200  return;
1201  }
1202  }
1203  else
1204  {
1206  }
1207  }
1208  else
1209  {
1211  }
1212  if(TrainMode == Timetable)
1213  {
1215  {
1217  }
1219  {
1221  }
1222  }
1223  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1224  // restart after stopped for train in front
1225  int NextElementPosition, NextEntryPos;
1226 
1227  if(LeadElement > -1) // if an exit continuation then not set
1228  {
1229  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1230  {
1232  }
1233  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1234  {
1235  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1236  {
1237  LeadExitPos = 1;
1238  }
1239  else
1240  {
1241  LeadExitPos = 3;
1242  }
1243  }
1244  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1245  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1246  }
1247  else
1248  {
1249  NextElementPosition = -1;
1250  NextEntryPos = -1;
1251  }
1252  if((NextElementPosition > -1) && (NextEntryPos > -1))
1253  // may be buffers or continuation so need this check
1254  {
1255 /*
1256  Check whether calling-on conditions met:-
1257  a) approaching train has stopped at a signal but not at a location;
1258  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1259  change of direction (cdt), remaining here (Frh), or under signaller control);
1260  c) at least 1 platform available for the approaching train;
1261  d) points (if any) set for direct route into platform;
1262  e) approaching train is to stop at station;
1263  f) no more facing signals between train and platform;
1264  g) [dropped g]
1265  h) train in front preventing route being set far enough to release stop signal;
1266  i) train in front not exiting at continuation;
1267  j) signal must be within 4km of the stop platform;
1268  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1269  l) no existing route conflicts with the route into the platform; and
1270  m) not failed or without power (these added at v2.10.0)
1271  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1272  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1273 */
1274  if(TrainMode == Timetable)
1275  {
1276  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1277  {
1278  CallingOnFlag = true;
1279  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1280  }
1281  else
1282  {
1283  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1284  {
1285  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1286  {
1288  }
1289  }
1290  CallingOnFlag = false;
1291  }
1292  }
1293  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1294  {
1295  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1296  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1297  // sets StoppedAtSignal again & train doesn't move
1298  StoppedAtSignal = false;
1299  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1300  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1301  // LeadMidLag and front of train was on LeadElement (after the current move)
1303  EntrySpeed = 0;
1305  FirstHalfMove = true;
1306  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1307  // NextElement is the element to be entered
1308  }
1310  {
1311  if(ClearToNextSignal(0))
1312  {
1313  StoppedForTrainInFront = false;
1314  BeingCalledOn = false;
1315  EntrySpeed = 0;
1317  FirstHalfMove = true;
1318  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1319  }
1320  else
1321  {
1322  if(TrainFailurePending) // ok, stopped so PlotElements set
1323  {
1324  TrainHasFailed(4);
1325  }
1326  Utilities->CallLogPop(1097);
1327  return;
1328  }
1329  }
1330  }
1331  if((Straddle == MidLag) && (LeadElement != -1))
1332  // later check only for Straddle == LeadMid, so need this check here for initial train start
1333  {
1335  }
1336 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1337  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1338  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1339  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1340  which could be when start as Snt.
1341  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1342  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1343  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1344  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1345  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1346  reached.
1347  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1348  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1349  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1350  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1351  sending a message to the performancelog.
1352 */
1353 
1354  if(TrainMode == Timetable)
1355  {
1357  {
1358  if(BeingCalledOn)
1359  {
1360  StoppedForTrainInFront = true;
1361  }
1363  {
1365  }
1367  {
1368  // value updated at every scheduled departure & arrival
1370  AnsiString StationName;
1372  {
1374  }
1376  {
1378  }
1379  else
1380  {
1381  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1382  }
1383  EntrySpeed = 0;
1387  FirstHalfMove = true;
1388  StoppedAtLocation = false;
1389 
1390  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1391  {
1392  StoppedWithoutPower = true;
1393  }
1394  if((NextElementPosition > -1) && (NextEntryPos > -1))
1395  // condition check added for SloughIECC error reported by James U
1396  {
1397  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1398  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1399  {
1400  StoppedAtSignal = true;
1401  if(!StoppedWithoutPower)
1402  // if stopped without power just keep existing background colour
1403  {
1405  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1406  }
1407  }
1408  }
1410  {
1411  TimeTimeLocArrived = false;
1412  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1413  // no warning for TimeTimeLoc departure
1414  }
1415  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1416  {
1417  LogAction(36, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1418  }
1419  else //must be TimeLoc departure
1420  {
1422  }
1423  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1424  DepartureTimeSet = false;
1425  // no need to set LastActionTime for a departure
1426  //deal here with departure pointer change, increment if SkippedDeparture
1427  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1428 
1429  if(SkippedDeparture)
1430  {
1433  TrainSkippedEvents = 0;
1434  SkippedDeparture = false;
1435  SkipPtrValue = 0;
1436  ActionsSkippedFlag = false;
1437  }
1438  else
1439  {
1441  }
1442  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1443  // note
1444 /*
1445  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1446  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1447  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1448  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1449  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1450  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1451  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1452 */
1454  {
1455  StoppedAtBuffers = true;
1456  }
1457  else if(!StoppedWithoutPower)
1458  // if buffers or no power, don't set values
1459  {
1461  {
1462  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1463  // NextElement is the element to be entered
1464  }
1465  else
1466  {
1468  // use LeadElement for an exit continuation
1469  }
1470  }
1471  }
1472  }
1473  }
1474  if(Straddle == LeadMidLag) //train on a half element
1475  {
1477  {
1478  Utilities->CallLogPop(654);
1479  return;
1480  }
1481  }
1482  else //train fully on 2 elements
1483  {
1485  {
1486  Utilities->CallLogPop(655);
1487  return;
1488  }
1489  }
1490  if((LeadElement > -1) && (MidElement > -1))
1491  {
1493  {
1494  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1495  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1496  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1497  SignallerStoppingFlag = false;
1498  StepForwardFlag = false;
1499  }
1500  }
1501  if(Stopped())
1502  // this is what prevents another movement if the train is stopped
1503  {
1504  if(TrainFailurePending) // ok, stopped so PlotElements set
1505  {
1506  TrainHasFailed(5);
1507  }
1508  BrakeRate = 0;
1509  Utilities->CallLogPop(656);
1510  return;
1511  }
1512 
1513  // HERE WHEN READY FOR NEXT MOVE
1514 
1515  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1516  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1517  if(Straddle == LeadMid) //fully on 2 elements
1518  {
1519  if(LeadElement > -1)
1520  {
1521  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1522  {
1524  if(TIF.TrackType == SignalPost)
1525  {
1526  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1527  int TIFExitPos = 0;
1528  if(TIFEntryPos == 0)
1529  {
1530  TIFExitPos = 1;
1531  }
1532  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1533  {
1534  SPADFlag = true; // user has to intervene to reset & restart after spad
1535  }
1536  }
1537  }
1538  }
1539  }
1540 
1541  // check for train in front & if so stop at next access (when train fully on element next to train)
1542  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1543  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1544  // variable TrainInFrontInSignallerModeFlag
1545  {
1546  if(LeadElement > -1)
1547  {
1548  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1549  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1550  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1551  // true if another train on NextEntryPos track whether bridge or not
1552  {
1553  StoppedForTrainInFront = true;
1554  }
1555  else
1556  {
1557  StoppedForTrainInFront = false;
1558  }
1559  }
1560  }
1561  if((Straddle == LeadMid) && SPADFlag)
1562  // give message + plot background when ready to move half past the signal
1563  {
1564  if(NextElementPosition > -1)
1565  {
1566  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1567  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1568  {
1569  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1571  // if goes past 2 signals then give message twice
1573  }
1574  }
1575  }
1576  if(Straddle == LeadMidLag)
1577  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1578  {
1579  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1580  if(SPADFlag)
1581  {
1582  if(ExitSpeedFull == 0)
1583  {
1584  StoppedAfterSPAD = true;
1585  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1586  }
1587  }
1589  {
1590  if(ExitSpeedFull == 0)
1591  {
1592  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1593  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1594  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1595  // is sent at the right time and once only.
1596  SignallerStopped = true;
1597  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1598  StepForwardFlag = false;
1599  SignallerStoppingFlag = false;
1600  TTrackElement TE;
1601  AnsiString Loc = "";
1602  bool LocNamed = false;
1603  if(LeadElement > -1)
1604  {
1605  TE = Track->TrackElementAt(782, LeadElement);
1606  if(TE.ActiveTrackElementName != "")
1607  {
1608  Loc = TE.ActiveTrackElementName;
1609  LocNamed = true;
1610  }
1611  else
1612  {
1613  Loc = "track element " + TE.ElementID;
1614  }
1615  }
1616  if((MidElement > -1) && !LocNamed)
1617  {
1618  TE = Track->TrackElementAt(783, MidElement);
1619  if(TE.ActiveTrackElementName != "")
1620  {
1621  Loc = TE.ActiveTrackElementName;
1622  LocNamed = true;
1623  }
1624  else if(Loc == "")
1625  {
1626  Loc = "track element " + TE.ElementID;
1627  }
1628  }
1629  if(Loc == "")
1630  {
1631  Loc = "outside railway";
1632  // must have stopped after left at a continuation (because both lead & mid == -1)
1633  }
1634  else
1635  {
1636  Loc = "at " + Loc;
1637  }
1638  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1639  }
1640  }
1641  if(LeadElement > -1) // if an exit continuation then not set
1642  {
1643  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1644  {
1646  }
1647  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1648  {
1649  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1650  {
1651  LeadExitPos = 1;
1652  }
1653  else
1654  {
1655  LeadExitPos = 3;
1656  }
1657  }
1658  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1659  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1660  }
1661  else
1662  {
1663  NextElementPosition = -1;
1664  NextEntryPos = -1;
1665  }
1668  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1669 
1670  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1671  {
1672  StoppedWithoutPower = true;
1673  }
1674  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1675  // may be buffers or continuation. SPADFlag added at v2.1.0
1676  // so don't override the SPAD colour & don't set StoppedAtSignal
1677  {
1678  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1679  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1680  {
1681  StoppedAtSignal = true;
1682  if(!StoppedWithoutPower)
1683  // leave background as is if no power, but set StoppedAtSignal
1684  {
1686  }
1687  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1688  }
1689  }
1690  if(!Stopped())
1691  {
1692  if((NextElementPosition > -1) && (NextEntryPos > -1))
1693  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1694  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1695  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1696  // function for fuller explanation
1697  {
1698  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1699  // NextElement is the element to be entered
1700  }
1701  // follow the continuation exits:-
1702  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1703  {
1705  // Use LeadElement for calcs if lead is a continuation
1706  }
1707  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1708  {
1710  // Use MidElement for calcs if mid is a continuation
1711  }
1712  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1713  {
1715  // Use LagElement for calcs if lag is a continuation
1716  }
1717  }
1718  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1719  if((AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute))
1720  // Trains may not be in a route
1721  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1722  {
1723  // NB if LeadElement == -1 then the above test returns NoRoute
1724  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1725  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1726  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1727  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1728  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1729  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1730  FirstPair.second).GetELink() == TempELink))
1731  {
1732  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1733  }
1734  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1735  SecondPair.second).GetELink() == TempELink))
1736  {
1737  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1738  }
1739  }
1740  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1741  // Trains may not be in a route
1742  {
1743  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1744  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1745  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1746  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1747  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1748  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1749  FirstPair.second).GetELink() == TempELink))
1750  {
1751  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1752  }
1753  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1754  SecondPair.second).GetELink() == TempELink))
1755  {
1756  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1757  }
1758  }
1759  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1760  // Trains may not be in a route
1761  {
1762  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1763  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1764  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1765  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1766  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1767  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1768  FirstPair.second).GetELink() == TempELink))
1769  {
1770  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1771  }
1772  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1773  SecondPair.second).GetELink() == TempELink))
1774  {
1775  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1776  }
1777  AllRoutes->CheckMapAndRoutes(8); // test
1778  }
1779  if(LagElement > -1)
1780  // not entering at a continuation so can deal with train leaving the lag element
1781  {
1783  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1784  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1785  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1786 
1787  TPrefDirElement PrefDirElement;
1788  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1789  // as this is a 16x16 graphic
1791  {
1793  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1794  }
1795  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1796  {
1797  int RouteNumber;
1798  TrainGone = true;
1799  // flag to indicate train to be deleted - outside this function
1801  {
1802  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1803  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1804  // calc distance from & inc last signal to exit
1805  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1806  int NewLastElement = 0, NewLastExitPos = 0;
1807  // need above because can't change LastElement & LastExitPos until both new values obtained
1808  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1809  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1810  LastElement).TrackType != Points))
1811  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1812  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1813  // leave CumDistance as it was in these circumstances.
1814  {
1815  if(LastExitPos < 2)
1816  {
1817  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1818  }
1819  else
1820  {
1821  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1822  }
1823  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1824  if(NewLastElement == -1)
1825  // this will catch buffers or any other connection failure
1826  {
1827  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1828  }
1829  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1830  if(NewLastExitPos == -1)
1831  {
1832  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1833  }
1834  LastElement = NewLastElement;
1835  LastExitPos = NewLastExitPos;
1836  }
1837  // if at signal add this in too
1838  if(CumDistance < 1200)
1839  {
1840  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1841  }
1842  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1843  // else use 1200m - CumDistance
1844  int FirstDistance = 0;
1845  if(CumDistance >= 1200)
1846  {
1847  FirstDistance = 100;
1848  }
1849  else
1850  {
1851  FirstDistance = 1200 - CumDistance;
1852  }
1853  if(FirstDistance < 100)
1854  {
1855  FirstDistance = 100; // don't allow < 100
1856  }
1857  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1858  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1859  if(ExitSpeedFull > 20.0)
1860  {
1861  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1862  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1863  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1864  // 4320.0 = 3.6 * 1200, .0 to make it a double
1865  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1866  }
1867  else
1868  {
1869  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1870  ContinuationAutoSigEntry.SecondDelay = 120.0;
1871  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1872  }
1873  ContinuationAutoSigEntry.AccessNumber = 0;
1874  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1876  {
1878  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1879  VectorIT++)
1880  {
1881  if(VectorIT->RouteNumber == RouteNumber)
1882  {
1883  // another train has passed out of same route so erase earlier entry
1884  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1885  break;
1886  }
1887  }
1888  }
1889  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1890  }
1892  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1893  Display->Update();
1894  // need to keep this since Update() not called for PlotSmallOutput as too slow
1895  Utilities->CallLogPop(659);
1896  return;
1897  }
1898  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1899  if(LeadElement > -1)
1900  {
1901  TTrackElement &TE = Track->TrackElementAt(224, LeadElement); //added at v2.13.0 for brevity
1902  if(TE.Config[LeadExitPos] == Signal)
1903  // changed to lead so reset early
1904  {
1905  LastSigPassedFailed = false; //used to cancel route elements up to next signal for autosigs route
1906  TE.Attribute = 0; // red
1907  int RouteNumber; //only used for autosigs routes
1908  //add chance to fail when train passes a signal
1909  if((random(Utilities->SignalChangeEventsPerFailure) == 0) && !TE.Failed && (Utilities->DelayMode != Nil) &&
1910  (TrainMode == Timetable) && !TE.CallingOnSet) //can't fail twice and a calling on signal can't fail
1911  {
1913  IFE.TVPos = LeadElement;
1914  TE.Failed = true;
1915  Display->WarningLog(19, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Signal failed at " + TE.ElementID);
1916  PerfLogForm->PerformanceLog(42, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Signal failed at " + TE.ElementID);
1917  TrainController->StopTTClockMessage(129, "Signal at " + TE.ElementID +
1918  " failed when changing aspect.\nTrains can only pass under signaller control.");
1919  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
1920  LastSigPassedFailed = true;
1921  //set repair time, random value in minutes between 10 and 179
1922  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
1923  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
1924  IFE.RepairTime = RepairTime;
1926  Track->FailedSignalsVector.push_back(IFE); //rearwards signals will be set when LagElement leaves signal
1927  }
1928  TE.CallingOnSet = false;
1929  // don't plot if zoomed out
1930  if(!Display->ZoomOutFlag)
1931  {
1933  }
1934  // covers signal resetting in same direction
1935  }
1936  }
1938  {
1939  AllRoutes->RebuildRailwayFlag = true; //added at v2.13.0 to replot signal after train left in case it had failed
1940  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1941  {
1942  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1943  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1944  TPrefDirElement PrefDirElement;
1945  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1947  {
1949  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1950  }
1952  LockedVectorNumber)))
1953  {
1955  }
1956  }
1957  }
1958  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1959  {
1960  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1962  // don't plot if zoomed out
1963  if(!Display->ZoomOutFlag)
1964  {
1966  }
1967  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1968  }
1970  {
1971  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1972  {
1973  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1974  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1975  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1976  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1977  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1978  int RouteNumber;
1980  // already know it's an autosigsroute, this is just to get the RouteNumber
1981  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1982  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1983  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1984  int RouteNumber2;
1986  // already know it's an autosigsroute, this is just to get the RouteNumber
1987  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1988  // note that if not in a route (as likely) then RouteNumber2 set to -1
1989  {
1990  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1991  // this was in the 1.3.0 addition but without the condition
1992  }
1993  // end of 1.3.2 addition
1994  // end of 1.3.0.addition
1995  }
1996  TPrefDirElement PrefDirElement;
1997  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1999  {
2001  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
2002  }
2003  }
2004  }
2005  }
2006  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
2007  if(Straddle == LeadMid)
2008  {
2009  AllowedToPassRedSignal = false;
2010  // if had been allowed to pass then at this point it will move half onto signal so can be reset
2011  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
2012  if(DerailPending)
2013  // set during last GetLeadElement, but only act on it when train fully on offending point
2014  // i.e. next time Straddle reaches LeadMid
2015  {
2016  Derailed = true;
2017  DerailPending = false;
2021  Utilities->CallLogPop(657);
2022  return;
2023  }
2030  Straddle = MidLag;
2031  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
2032  // LeadElement during this function (note that if stopped at signal then won't get this far)
2033  if(LeadElement > -1)
2034  {
2036  // i.e an exit continuation only
2037  // if don't exclude entry continuations then can't progress past it
2038  {
2039  LeadElement = -1;
2040  }
2041  else
2042  {
2043  GetLeadElement(0);
2044  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2046  if(Stopped())
2047  {
2048  if(TrainFailurePending) // ok, stopped so PlotElements set
2049  {
2050  TrainHasFailed(6);
2051  }
2052  Utilities->CallLogPop(658);
2053  return; // i.e. don't move forward one step if next element is a red signal
2054  }
2055  }
2056  }
2057  }
2058  if(LagElement > -1)
2059  {
2060  // below are the actions required at both half moves for LagElement > -1
2062 
2063  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2064  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2065  // need to do this for each half element
2066 
2067  TPrefDirElement PrefDirElement;
2068  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2069  {
2070  int RouteNumber; // holder for call below - not used
2072  {
2073  if(Utilities->clTransparent == TColor(0xFFFFFF))
2074  // change to black for a white background
2075  {
2077  // only applies for AutoSigs Route in case was locked & timed out
2078  }
2079  else
2080  // change to white for a dark background
2081  {
2083  // only applies for AutoSigs Route in case was locked & timed out
2084  }
2086  }
2087  }
2089  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2090  // or a train on the opposite track - needs to be replotted
2091  }
2092  // update all array values
2093  HOffset[3] = HOffset[2];
2094  HOffset[2] = HOffset[1];
2095  HOffset[1] = HOffset[0];
2096  VOffset[3] = VOffset[2];
2097  VOffset[2] = VOffset[1];
2098  VOffset[1] = VOffset[0];
2099  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2100 
2101  BackgroundPtr[3] = BackgroundPtr[2];
2102  BackgroundPtr[2] = BackgroundPtr[1];
2103  BackgroundPtr[1] = BackgroundPtr[0];
2104  BackgroundPtr[0] = TempPtr;
2105 
2106  // update headcode graphics depending on Lead entry value
2107  if(LeadElement > -1) // if Lead is -1 then stays as is
2108  {
2110  {
2111  for(int x = 0; x < 4; x++)
2112  {
2113  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2114  }
2115  }
2116  else
2117  {
2118  for(int x = 0; x < 4; x++)
2119  {
2121  }
2122  }
2123  }
2124  if(TrainMode == Timetable)
2125  {
2127  }
2128  else
2129  {
2131  }
2133 
2134  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2135  if(LeadElement > -1)
2136  {
2137  if(Straddle == MidLag)
2138  // just about to move half onto the new lead element
2139  {
2141  // pick up new background bitmap [0]
2143  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2144  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2145  // check if own ID for entry at continuation, else crashes into itself!
2146  {
2147  // OK if crossing on a bridge
2148  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2149  if(OtherTrainEntryPos == -1)
2150  {
2151  throw Exception("Error - OtherTrainEntryPos not set");
2152  }
2153  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2154  // LeadEntryPos for rear end crashes
2155  (LeadExitPos == OtherTrainEntryPos))
2156  // LeadExitPos for head-on crashes
2157  {
2159  Crashed = true; // only set if Straddle = MidLag
2160  CallingOnFlag = false;
2161  // in case was set, need to disable call on if call on button had been pressed
2162  }
2163  }
2164  else if(MidElement > -1) // will be -1 for continuation entries
2165  {
2166  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2167  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2168  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2169  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2170  int OtherTrainID = -1;
2171  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2172  {
2173  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2174  {
2175  TrainCrashedInto = OtherTrainID;
2176  Crashed = true; // only set if Straddle = MidLag
2177  CallingOnFlag = false;
2178  // in case was set, need to disable call on if call on button had been pressed
2179  }
2180  }
2181  }
2182  }
2183  else
2184  {
2186  // pick up new background bitmap [0]
2188  }
2189  PlotElement[0] = LeadElement;
2191  PlotTrainGraphic(12, 0, Display);
2192  }
2193  if(MidElement > -1)
2194  {
2195  PlotElement[2] = MidElement;
2197  PlotTrainGraphic(1, 2, Display);
2198  }
2199  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2200  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2201  if(Straddle == MidLag)
2202  {
2203  if(MidElement > -1)
2204  {
2205  PlotElement[1] = MidElement;
2207  PlotTrainGraphic(2, 1, Display);
2208  }
2209  if(LagElement > -1)
2210  {
2211  PlotElement[3] = LagElement;
2213  PlotTrainGraphic(3, 3, Display);
2214  }
2215  }
2216  else // Straddle == LeadMidLag
2217  {
2218  if(LeadElement > -1)
2219  {
2220  PlotElement[1] = LeadElement;
2222  PlotTrainGraphic(4, 1, Display);
2223  }
2224  if(MidElement > -1)
2225  {
2226  PlotElement[3] = MidElement;
2228  PlotTrainGraphic(5, 3, Display);
2229  }
2230  }
2231  if(Crashed)
2232  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2233  {
2238  // in case was set, need to disable call on if call on button had been pressed
2245  Straddle = LeadMidLag;
2246  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2247  Display->Update();
2248  // resurrected when Update() dropped from PlotOutput etc
2249  Utilities->CallLogPop(660);
2250  return;
2251  }
2252  // deal here with station stops & pass times after all replotting done but before Straddle changed
2253  if(TrainMode == Timetable)
2254  {
2255  if(Straddle == LeadMidLag)
2256  {
2257  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2258  {
2259  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2260  // to point to the location arrival entry - before a change of direction
2261  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2262  bool StopRequired = false;
2263  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2264  if(TTVPos > -1) // -1 if can't find it or if name is ""
2265  {
2266  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2267  // or a station where next element contains a train or a stop signal, if so
2268  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2269  // to test the actual track the train is on since it can't be a platform
2270  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2271  TTrackElement NextTrackElement; // default for now
2272  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2273  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2274  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2275  int NextElementEntryPos = -1;
2276  int NextElementExitPos = -1;
2277  bool TrainOnNextElement = false;
2278  bool StopSignalAtNextElement = false;
2279  if(ForwardConnection)
2280  // if no forward connection can't derive anything from it without errors
2281  {
2282  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2283  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2284  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2285  // this is only for signals so no need to worry about points ambiguity
2286  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2287  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2288  }
2289  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2290  {
2291  if(TTVPos > 0)
2292  {
2294  ActionVectorEntryPtr += TTVPos;
2295  }
2296  if(StopRequired)
2297  {
2298  StoppedAtLocation = true;
2299  StoppedAtSignal = false;
2300  // may have been set earlier at line 925 so need to reset as
2301  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2302  // in zoom out mode
2303  if(!TrainFailed)
2304  {
2306  // pale green
2307  }
2309  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2311  {
2312  TimeTimeLocArrived = true;
2313  // used in case of later signaller control, when need to know
2314  // whether had arrived or not, to avoid sending the arrival
2315  // message twice, see TInterface::TimetableControl1Click
2316  }
2317  }
2318  else
2319  {
2321  }
2323  {
2325  }
2326  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2328  }
2329  }
2330  }
2331  }
2332  }
2333  if(Straddle == MidLag)
2334  {
2335  Straddle = LeadMidLag;
2336  FirstHalfMove = false;
2337  }
2338  else if(Straddle == LeadMidLag)
2339  {
2340  Straddle = LeadMid;
2341  FirstHalfMove = true;
2342  }
2343  else if(Straddle == LeadMid)
2344  {
2345  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2346  }
2347  if(TrainFailurePending) // ok, moving but PlotElements set above
2348  {
2349  TrainHasFailed(7);
2350  }
2351  Display->Update();
2352  // need to keep this since Update() not called for PlotSmallOutput as too slow
2353  Utilities->CallLogPop(661);
2354 }
2355 
2356 // ----------------------------------------------------------------------------
2357 
2358 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2359 {
2360  switch(CodeChar)
2361  {
2362  case '0':
2363  return(RailGraphics->Code0);
2364 
2365  case '1':
2366  return(RailGraphics->Code1);
2367 
2368  case '2':
2369  return(RailGraphics->Code2);
2370 
2371  case '3':
2372  return(RailGraphics->Code3);
2373 
2374  case '4':
2375  return(RailGraphics->Code4);
2376 
2377  case '5':
2378  return(RailGraphics->Code5);
2379 
2380  case '6':
2381  return(RailGraphics->Code6);
2382 
2383  case '7':
2384  return(RailGraphics->Code7);
2385 
2386  case '8':
2387  return(RailGraphics->Code8);
2388 
2389  case '9':
2390  return(RailGraphics->Code9);
2391 
2392  case 'A':
2393  return(RailGraphics->CodeA);
2394 
2395  case 'B':
2396  return(RailGraphics->CodeB);
2397 
2398  case 'C':
2399  return(RailGraphics->CodeC);
2400 
2401  case 'D':
2402  return(RailGraphics->CodeD);
2403 
2404  case 'E':
2405  return(RailGraphics->CodeE);
2406 
2407  case 'F':
2408  return(RailGraphics->CodeF);
2409 
2410  case 'G':
2411  return(RailGraphics->CodeG);
2412 
2413  case 'H':
2414  return(RailGraphics->CodeH);
2415 
2416  case 'I':
2417  return(RailGraphics->CodeI);
2418 
2419  case 'J':
2420  return(RailGraphics->CodeJ);
2421 
2422  case 'K':
2423  return(RailGraphics->CodeK);
2424 
2425  case 'L':
2426  return(RailGraphics->CodeL);
2427 
2428  case 'M':
2429  return(RailGraphics->CodeM);
2430 
2431  case 'N':
2432  return(RailGraphics->CodeN);
2433 
2434  case 'O':
2435  return(RailGraphics->CodeO);
2436 
2437  case 'P':
2438  return(RailGraphics->CodeP);
2439 
2440  case 'Q':
2441  return(RailGraphics->CodeQ);
2442 
2443  case 'R':
2444  return(RailGraphics->CodeR);
2445 
2446  case 'S':
2447  return(RailGraphics->CodeS);
2448 
2449  case 'T':
2450  return(RailGraphics->CodeT);
2451 
2452  case 'U':
2453  return(RailGraphics->CodeU);
2454 
2455  case 'V':
2456  return(RailGraphics->CodeV);
2457 
2458  case 'W':
2459  return(RailGraphics->CodeW);
2460 
2461  case 'X':
2462  return(RailGraphics->CodeX);
2463 
2464  case 'Y':
2465  return(RailGraphics->CodeY);
2466 
2467  case 'Z':
2468  return(RailGraphics->CodeZ);
2469 
2470  case 'a':
2471  return(RailGraphics->Code_a);
2472 
2473  case 'b':
2474  return(RailGraphics->Code_b);
2475 
2476  case 'c':
2477  return(RailGraphics->Code_c);
2478 
2479  case 'd':
2480  return(RailGraphics->Code_d);
2481 
2482  case 'e':
2483  return(RailGraphics->Code_e);
2484 
2485  case 'f':
2486  return(RailGraphics->Code_f);
2487 
2488  case 'g':
2489  return(RailGraphics->Code_g);
2490 
2491  case 'h':
2492  return(RailGraphics->Code_h);
2493 
2494  case 'i':
2495  return(RailGraphics->Code_i);
2496 
2497  case 'j':
2498  return(RailGraphics->Code_j);
2499 
2500  case 'k':
2501  return(RailGraphics->Code_k);
2502 
2503  case 'l':
2504  return(RailGraphics->Code_l);
2505 
2506  case 'm':
2507  return(RailGraphics->Code_m);
2508 
2509  case 'n':
2510  return(RailGraphics->Code_n);
2511 
2512  case 'o':
2513  return(RailGraphics->Code_o);
2514 
2515  case 'p':
2516  return(RailGraphics->Code_p);
2517 
2518  case 'q':
2519  return(RailGraphics->Code_q);
2520 
2521  case 'r':
2522  return(RailGraphics->Code_r);
2523 
2524  case 's':
2525  return(RailGraphics->Code_s);
2526 
2527  case 't':
2528  return(RailGraphics->Code_t);
2529 
2530  case 'u':
2531  return(RailGraphics->Code_u);
2532 
2533  case 'v':
2534  return(RailGraphics->Code_v);
2535 
2536  case 'w':
2537  return(RailGraphics->Code_w);
2538 
2539  case 'x':
2540  return(RailGraphics->Code_x);
2541 
2542  case 'y':
2543  return(RailGraphics->Code_y);
2544 
2545  case 'z':
2546  return(RailGraphics->Code_z);
2547 
2548  default:
2549  return(RailGraphics->TempHeadCode);
2550  }
2551 }
2552 
2553 // ----------------------------------------------------------------------------
2554 
2555 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2556 {
2557  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2558  if(Code.Length() != 4)
2559  {
2560  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2561  }
2562  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2563  {
2564  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2565  }
2566  if(BackgroundColour != clB5G5R5)
2567  // i.e. not the basic graphic colour as loaded from resource file
2568  {
2569  for(int x = 0; x < 4; x++)
2570  {
2572  }
2573  }
2574  Utilities->CallLogPop(1484);
2575 }
2576 
2577 // ----------------------------------------------------------------------------
2578 
2579 void TTrain::GetLeadElement(int Caller)
2580 // assumes Mid & Lag already set, sets LeadElement,
2581 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2582 {
2583  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2584  DerailPending = false;
2588  {
2589  // attr 0=straight, - links 0 & 1 (0 = lead)
2590  // attr 1=diverging, - links 2 & 3 (2 = lead)
2591  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2592  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2593 
2594  // if enter at lead, exit at whatever attr set at
2595  // if enter at lag, exit at lead, but set derail wrt attribute
2596  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2597  {
2598  LeadExitPos = 1;
2599  }
2600 
2601  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2602  // best to be on safe side
2603  else if(LeadEntryPos == 0)
2604  {
2605  LeadEntryPos = 2;
2606  LeadExitPos = 3;
2607  }
2608  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2609  {
2610  LeadEntryPos = 0;
2611  LeadExitPos = 1;
2612  }
2613  else if(LeadEntryPos == 2)
2614  {
2615  LeadExitPos = 3;
2616  }
2617  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2618  {
2619  LeadExitPos = 0;
2620  }
2621  else if(LeadEntryPos == 1)
2622  {
2623  LeadExitPos = 0;
2624  DerailPending = true;
2625  }
2626  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2627  {
2628  LeadExitPos = 0;
2629  DerailPending = true;
2630  }
2631  else if(LeadEntryPos == 3)
2632  {
2633  LeadExitPos = 0;
2634  }
2635  }
2636  else if(LeadEntryPos == 0)
2637  {
2638  LeadExitPos = 1;
2639  }
2640  else if(LeadEntryPos == 1)
2641  {
2642  LeadExitPos = 0;
2643  }
2644  else if(LeadEntryPos == 2)
2645  {
2646  LeadExitPos = 3;
2647  }
2648  else if(LeadEntryPos == 3)
2649  {
2650  LeadExitPos = 2;
2651  }
2652  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2653 /* signal check moved to Update() function
2654  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2655  && (TrackElement.Attribute == 0))//0 = red
2656  {
2657  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2658  }
2659  else
2660  {
2661  StoppedAtSignal = false;
2662  }
2663 */
2664  Utilities->CallLogPop(662);
2665 }
2666 
2667 // ----------------------------------------------------------------------------
2668 
2669 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2670 {
2671  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2672  switch(Link)
2673  {
2674  case 1:
2675  {
2676  HOffset = 0;
2677  VOffset = 0;
2678  break;
2679  }
2680 
2681  case 2:
2682  {
2683  HOffset = 4;
2684  VOffset = 0;
2685  break;
2686  }
2687 
2688  case 3:
2689  {
2690  HOffset = 8;
2691  VOffset = 0;
2692  break;
2693  }
2694 
2695  case 4:
2696  {
2697  HOffset = 0;
2698  VOffset = 4;
2699  break;
2700  }
2701 
2702  case 6:
2703  {
2704  HOffset = 8;
2705  VOffset = 4;
2706  break;
2707  }
2708 
2709  case 7:
2710  {
2711  HOffset = 0;
2712  VOffset = 8;
2713  break;
2714  }
2715 
2716  case 8:
2717  {
2718  HOffset = 4;
2719  VOffset = 8;
2720  break;
2721  }
2722 
2723  case 9:
2724  {
2725  HOffset = 8;
2726  VOffset = 8;
2727  break;
2728  }
2729 
2730  default:
2731  {
2732  throw Exception("Error in GetOffsetValues - Link value wrong");
2733  }
2734  }
2735  Utilities->CallLogPop(674);
2736 }
2737 
2738 // ---------------------------------------------------------------------------
2739 
2740 bool TTrain::LowEntryValue(int EntryLink) const
2741 {
2742 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2743  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2744 */
2745  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2746  {
2747  return(true);
2748  }
2749  else
2750  {
2751  return(false);
2752  }
2753 }
2754 
2755 // ---------------------------------------------------------------------------
2756 
2757 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2758 {
2759  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2760  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2761  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2762  // default values
2763  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2764 
2765  TAllRoutes::TRouteType RouteType;
2766 
2767  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2768 
2769  TRect SourceRect, DestRect;
2770 
2771  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2772  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2773  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2774  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2775 
2776  TempGraphic->PixelFormat = pf8bit;
2777  TempGraphic->Width = 16;
2778  TempGraphic->Height = 16;
2779  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2780 
2781  if(TempElement.TrackType == Points)
2782  {
2783  TempGraphic->Assign(TempElement.GraphicPtr);
2784  TempGraphic->Transparent = true;
2785  TempGraphic->TransparentColor = Utilities->clTransparent;
2786  if(RouteType == TAllRoutes::AutoSigsRoute)
2787  {
2788  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2789  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2790  }
2791  else
2792  {
2793  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2794  }
2795  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2796  }
2797  else if(TempElement.TrackType == GapJump) // plot set gap
2798  {
2799  if(TempElement.SpeedTag == 88)
2800  {
2801  TempGraphic->Assign(RailGraphics->gl88set);
2802  }
2803  else if(TempElement.SpeedTag == 89)
2804  {
2805  TempGraphic->Assign(RailGraphics->gl89set);
2806  }
2807  else if(TempElement.SpeedTag == 90)
2808  {
2809  TempGraphic->Assign(RailGraphics->gl90set);
2810  }
2811  else if(TempElement.SpeedTag == 91)
2812  {
2813  TempGraphic->Assign(RailGraphics->gl91set);
2814  }
2815  else if(TempElement.SpeedTag == 92)
2816  {
2817  TempGraphic->Assign(RailGraphics->gl92set);
2818  }
2819  else if(TempElement.SpeedTag == 93)
2820  {
2821  TempGraphic->Assign(RailGraphics->bm93set);
2822  }
2823  else if(TempElement.SpeedTag == 94)
2824  {
2825  TempGraphic->Assign(RailGraphics->bm94set);
2826  }
2827  else if(TempElement.SpeedTag == 95)
2828  {
2829  TempGraphic->Assign(RailGraphics->gl95set);
2830  }
2831  TempGraphic->Transparent = true;
2832  TempGraphic->TransparentColor = Utilities->clTransparent;
2833  if(RouteType == TAllRoutes::AutoSigsRoute)
2834  {
2835  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2836  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2837  }
2838  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2839  }
2840  // new for version 0.6
2841  else if(TempElement.TrackType == SignalPost)
2842  {
2843  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2844  {
2845  for(int x = 0; x < 40; x++)
2846  {
2847  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2848  // need to stop aspect
2849  {
2850  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2851  break;
2852  }
2853  }
2854  }
2855  else // normal signal
2856  {
2857  TempGraphic->Assign(TempElement.GraphicPtr);
2858  // GraphicPtr set to normal signal in a signal track element
2859  }
2860  TempGraphic->Transparent = true;
2861  TempGraphic->TransparentColor = Utilities->clTransparent;
2862  if(RouteType == TAllRoutes::AutoSigsRoute)
2863  {
2864  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2865  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2866  }
2867  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2868  }
2869  else
2870  {
2871  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2872  // can't name points gaps or signals so 'else' OK
2873  bool FoundFlag;
2874  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2875  if(FoundFlag)
2876  {
2878  {
2879  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2880  TempGraphic->Assign(RailGraphics->bmName);
2881  TempGraphic->Transparent = true;
2882  TempGraphic->TransparentColor = Utilities->clTransparent;
2883  if(RouteType == TAllRoutes::AutoSigsRoute)
2884  {
2885  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2886  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2887  }
2888  else
2889  {
2890  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2891  }
2892  // draw track on top
2893  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2894  }
2895  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2896  {
2897  TempGraphic->Assign(TempElement.GraphicPtr);
2898  TempGraphic->Transparent = true;
2899  TempGraphic->TransparentColor = Utilities->clTransparent;
2900  // note that can't be an AutoSigsRoute
2901  // now overlay the LC central portion
2902  int BDVectorPos = -1; //not used
2903  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2904  {
2905  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2906  }
2907  else
2908  {
2909  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2910  }
2911  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2912  }
2913  else
2914  {
2915  TempGraphic->Assign(TempElement.GraphicPtr);
2916  TempGraphic->Transparent = true;
2917  TempGraphic->TransparentColor = Utilities->clTransparent;
2918  if(RouteType == TAllRoutes::AutoSigsRoute)
2919  {
2920  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2921  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2922  }
2923  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2924  }
2925  }
2926  else
2927  {
2928  TempGraphic->Assign(TempElement.GraphicPtr);
2929  TempGraphic->Transparent = true;
2930  TempGraphic->TransparentColor = Utilities->clTransparent;
2931  if(RouteType == TAllRoutes::AutoSigsRoute)
2932  {
2933  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2934  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2935  }
2936  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2937  }
2938  }
2939  delete TempGraphic;
2940  Utilities->CallLogPop(675);
2941 }
2942 
2943 // ---------------------------------------------------------------------------
2944 
2945 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2946 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2947 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2948 /*
2949 
2950  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2951  {
2952  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2953  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2954  TAllRoutes::TRouteType RouteType;
2955  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2956  // default values
2957  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2958  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2959  TRect SourceRect, DestRect, ScreenSourceRect;
2960  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2961 
2962  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2963  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2964  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2965 
2966  //add text & user graphics if any to *GraphicPtr prior to adding the track
2967  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2968  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2969  int Right = Left + 8;
2970  int Bottom = Top + 8;
2971  ScreenSourceRect.init(Left, Top, Right, Bottom);
2972  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2973  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2974 
2975  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2976  TempGraphic->PixelFormat = pf8bit;
2977  TempGraphic->Width = 16;
2978  TempGraphic->Height = 16;
2979 
2980  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2981  SourceGraphic->PixelFormat = pf8bit;
2982  SourceGraphic->Width = 16;
2983  SourceGraphic->Height = 16;
2984  SourceGraphic->Transparent = true;
2985  SourceGraphic->TransparentColor = Utilities->clTransparent;
2986 
2987  if (TempElement.TrackType == Points)
2988  {
2989  TempGraphic->Assign(TempElement.GraphicPtr);
2990  TempGraphic->Transparent = true;
2991  TempGraphic->TransparentColor = Utilities->clTransparent;
2992  if (RouteType == TAllRoutes::AutoSigsRoute)
2993  {
2994  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2995  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2996  }
2997  else
2998  {
2999  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
3000  }
3001  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3002  }
3003  else if (TempElement.TrackType == GapJump) // plot set gap
3004  {
3005  if (TempElement.SpeedTag == 88)
3006  TempGraphic->Assign(RailGraphics->gl88set);
3007  else if (TempElement.SpeedTag == 89)
3008  TempGraphic->Assign(RailGraphics->gl89set);
3009  else if (TempElement.SpeedTag == 90)
3010  TempGraphic->Assign(RailGraphics->gl90set);
3011  else if (TempElement.SpeedTag == 91)
3012  TempGraphic->Assign(RailGraphics->gl91set);
3013  else if (TempElement.SpeedTag == 92)
3014  TempGraphic->Assign(RailGraphics->gl92set);
3015  else if (TempElement.SpeedTag == 93)
3016  TempGraphic->Assign(RailGraphics->bm93set);
3017  else if (TempElement.SpeedTag == 94)
3018  TempGraphic->Assign(RailGraphics->bm94set);
3019  else if (TempElement.SpeedTag == 95)
3020  TempGraphic->Assign(RailGraphics->gl95set);
3021  TempGraphic->Transparent = true;
3022  TempGraphic->TransparentColor = Utilities->clTransparent;
3023  if (RouteType == TAllRoutes::AutoSigsRoute) {
3024  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3025  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3026  }
3027  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3028  }
3029  // new for version 0.6
3030  else if (TempElement.TrackType == SignalPost)
3031  {
3032  if (TempElement.SigAspect == TTrackElement::GroundSignal)
3033  {
3034  for (int x = 0; x < 40; x++)
3035  {
3036  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
3037  // need to stop aspect
3038  {
3039  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
3040  break;
3041  }
3042  }
3043  }
3044  else // normal signal
3045  {
3046  TempGraphic->Assign(TempElement.GraphicPtr);
3047  // GraphicPtr set to normal signal in a signal track element
3048  }
3049  TempGraphic->Transparent = true;
3050  TempGraphic->TransparentColor = Utilities->clTransparent;
3051  if (RouteType == TAllRoutes::AutoSigsRoute) {
3052  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3053  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3054  }
3055  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3056  }
3057  else {
3058  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3059  // can't name points gaps or signals so 'else' OK
3060  bool FoundFlag;
3061  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3062  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3063  if (FoundFlag)
3064  {
3065  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3066  {
3067  GraphicPtr->Canvas->CopyRect(DestRect,
3068  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3069  TempGraphic->Assign(RailGraphics->bmName);
3070  TempGraphic->Transparent = true;
3071  TempGraphic->TransparentColor = Utilities->clTransparent;
3072  if (RouteType == TAllRoutes::AutoSigsRoute)
3073  {
3074  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3075  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3076  }
3077  else
3078  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3079  // draw track on top
3080  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3081  SourceRect);
3082  }
3083  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3084  TempGraphic->Assign(TempElement.GraphicPtr);
3085  TempGraphic->Transparent = true;
3086  TempGraphic->TransparentColor = Utilities->clTransparent;
3087  // note that can't be an AutoSigsRoute
3088  // now overlay the LC central portion
3089  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3090  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3091  SourceRect);
3092  }
3093  else {
3094  TempGraphic->Assign(TempElement.GraphicPtr);
3095  TempGraphic->Transparent = true;
3096  TempGraphic->TransparentColor = Utilities->clTransparent;
3097  if (RouteType == TAllRoutes::AutoSigsRoute) {
3098  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3099  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3100  }
3101  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3102  SourceRect);
3103  }
3104  }
3105  else {
3106  TempGraphic->Assign(TempElement.GraphicPtr);
3107  TempGraphic->Transparent = true;
3108  TempGraphic->TransparentColor = Utilities->clTransparent;
3109  if (RouteType == TAllRoutes::AutoSigsRoute) {
3110  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3111  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3112  }
3113  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3114  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3115  }
3116  }
3117  delete TempGraphic;
3118  delete SourceGraphic;
3119  Utilities->CallLogPop();
3120  }
3121 */
3122 // ---------------------------------------------------------------------------
3123 
3124 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3125 {
3126  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3127  if(PlotElement[ArrayNumber] == -1)
3128  {
3129  Utilities->CallLogPop(676);
3130  return; // not plotted yet
3131  }
3132  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3133  // set before plot so gap flashing stops first
3134  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3135  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3136  // Only need to set ID for leading element, stays set until train finally leaves the element
3137  Plotted = true;
3138  Utilities->CallLogPop(677);
3139 }
3140 
3141 // ---------------------------------------------------------------------------
3142 
3143 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3144 {
3145  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3146  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3147 }
3148 
3149 // ---------------------------------------------------------------------------
3150 
3151 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3152 {
3153  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3154  HeadCode);
3155  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3156  {
3157  Utilities->CallLogPop(678);
3158  return(true);
3159  }
3160  else
3161  {
3162  Utilities->CallLogPop(679);
3163  return(false);
3164  }
3165 }
3166 
3167 // ---------------------------------------------------------------------------
3168 
3169 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3170 {
3171  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3172  "," + HeadCode);
3173  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3174  {
3175  Utilities->CallLogPop(680);
3176  return(true);
3177  }
3178  else
3179  {
3180  Utilities->CallLogPop(681);
3181  return(false);
3182  }
3183 }
3184 
3185 // ---------------------------------------------------------------------------
3186 
3187 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3188 // test whether this train on a bridge on trackpos 0 & 1
3189 {
3190  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3191  HeadCode);
3192  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3193  {
3194  Utilities->CallLogPop(682);
3195  return(false);
3196  }
3197  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3199  {
3201  {
3202  throw Exception("Error, same train on two different bridge tracks");
3203  }
3204  else
3205  {
3206  Utilities->CallLogPop(684);
3207  return(true);
3208  }
3209  }
3210  else
3211  {
3212  Utilities->CallLogPop(685);
3213  return(false);
3214  }
3215 }
3216 
3217 // ---------------------------------------------------------------------------
3218 
3219 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3220 // test whether this train on a bridge on trackpos 2 & 3
3221 {
3222  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3223  HeadCode);
3224  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3225  {
3226  Utilities->CallLogPop(686);
3227  return(false);
3228  }
3229  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3231  {
3232  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3233  Utilities->CallLogPop(687);
3234  return(true);
3235  }
3236  else
3237  {
3238  Utilities->CallLogPop(688);
3239  return(false);
3240  }
3241 }
3242 
3243 // ---------------------------------------------------------------------------
3244 
3245 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3246 {
3247  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3248  AnsiString(EntryPos) + "," + HeadCode);
3249  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3250 
3251  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3252  if(Track->GapFlashFlag)
3253  {
3255  {
3258  Track->GapFlashFlag = false;
3259  }
3260  }
3261  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3262  {
3263  if(EntryPos == -1)
3264  {
3265  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3266  }
3267  if(EntryPos < 2)
3268  {
3270  }
3271  else
3272  {
3274  }
3275  }
3276  Utilities->CallLogPop(690);
3277 }
3278 
3279 // ---------------------------------------------------------------------------
3280 
3281 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3282 {
3283  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3284  AnsiString(EntryPos) + "," + HeadCode);
3285  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3286  {
3287  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3288  }
3289  else
3290  {
3291  if(EntryPos == -1)
3292  {
3293  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3294  }
3295  if(EntryPos < 2)
3296  {
3297  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
3298  }
3299  else
3300  {
3301  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
3302  }
3303  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
3304  // i.e. other train on track 2&3
3305  {
3306  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
3307  }
3308  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
3309  // i.e. other train on track 1&2
3310  {
3311  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
3312  }
3313  else
3314  {
3315  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3316  }
3317  }
3318  Utilities->CallLogPop(691);
3319 }
3320 
3321 // ---------------------------------------------------------------------------
3322 
3323 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3324 {
3325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3326  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3327  int LockedVectorNumber;
3328 
3329  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3330  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3331  {
3332  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3333  Utilities->CallLogPop(692);
3334  return;
3335  }
3336  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3337  // i.e other track is in a marked route
3338  // LinkPos doesn't have to be the entry position for the above check
3339  {
3340  TRect SourceRect, DestRect;
3341  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3342  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3343  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3344  // identify the route element for the other track
3345  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3346  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3347  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3348  int FirstELink, SecondELink = -1;
3349  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3350  // must be at least one
3351  if(RoutePair2.first > -1)
3352  {
3353  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3354  }
3355  TPrefDirElement RouteElement;
3356  // Graphics::TBitmap *RouteGraphic;
3357  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3358  // i.e. other track is in RoutePair2
3359  {
3360  if(SecondELink == -1)
3361  {
3362  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3363  }
3364  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3365  // error if both have same Link number
3366  {
3367  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3368  }
3369  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3370  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3371  }
3372  else // other track is in RoutePair1
3373  {
3374  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3375  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3376  }
3377  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3378  DestGraphic->PixelFormat = pf8bit;
3379  DestGraphic->Width = 8;
3380  DestGraphic->Height = 8;
3381  DestGraphic->Transparent = true;
3382  // has to be transparent or will overwrite the track that the train has just left
3383  DestGraphic->TransparentColor = Utilities->clTransparent;
3384  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3385  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3386  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3387  // plot locked route marker for other route if appropriate
3388  TPrefDirElement PrefDirElement; // holder for next call, unused
3389  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3390  if(StraddleValue == LeadMidLag)
3391  {
3393  PrefDirElement, LockedVectorNumber))
3394  {
3395  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3396  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3397  }
3398  }
3399  delete DestGraphic;
3400  }
3401  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3402  // also can only be a bridge or trains either have already or soon will crash
3403  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3404  {
3405  Utilities->CallLogPop(695);
3406  return;
3407  }
3408  if(ElementEntryPos > 1) // other train is on track 01
3409  {
3411  {
3413  }
3414  }
3415  else // other train is on track 23
3416  {
3418  {
3420  }
3421  }
3422  Utilities->CallLogPop(696);
3423 }
3424 
3425 // ---------------------------------------------------------------------------
3426 
3427 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3428 {
3429  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3430  AnsiString(EntryPos) + "," + HeadCode);
3431  int RouteNumber;
3432  bool WrongRoute = false;
3433  TPrefDirElement RouteElement;
3435  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3436 
3437  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3438  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3439  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3440  {
3441  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3442  {
3443  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3444  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3445  {
3446  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3447  {
3448  // don't call for stub end routes
3450  }
3451  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3452  Utilities->CallLogPop(697);
3453  return;
3454  }
3455  }
3456  // also need to check for a route on a crossing diagonal
3457  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3458  int LinkNumber = TrackElement.Link[EntryPos];
3459  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3460  {
3461  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3462  {
3463  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3464  bool LogActionErrorCalled = false;
3465  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3466  if(LinkNumber == 1)
3467  {
3468  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3469  {
3470  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3471  {
3472  // don't call for stub end routes
3474  LogActionErrorCalled = true;
3475  }
3476  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3477  }
3478  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3479  // not else in case have different routes on each diagonal, though shouldn't be possible
3480  {
3481  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3482  {
3483  // don't call for stub end routes
3485  }
3486  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3487  }
3488  }
3489 
3490  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3491  else if(LinkNumber == 3)
3492  {
3493  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3494  {
3495  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3496  {
3497  // don't call for stub end routes
3499  LogActionErrorCalled = true;
3500  }
3501  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3502  }
3503  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3504  // not else in case have different routes on each diagonal, though shouldn't be possible
3505  {
3506  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3507  {
3508  // don't call for stub end routes
3510  }
3511  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3512  }
3513  }
3514 
3515  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3516  else if(LinkNumber == 7)
3517  {
3518  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3519  {
3520  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3521  {
3522  // don't call for stub end routes
3524  LogActionErrorCalled = true;
3525  }
3526  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3527  }
3528  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3529  // not else in case have different routes on each diagonal, though shouldn't be possible
3530  {
3531  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3532  {
3533  // don't call for stub end routes
3535  }
3536  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3537  }
3538  }
3539 
3540  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3541  else if(LinkNumber == 9)
3542  {
3543  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3544  {
3545  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3546  {
3547  // don't call for stub end routes
3549  LogActionErrorCalled = true;
3550  }
3551  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3552  }
3553  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3554  // not else in case have different routes on each diagonal, though shouldn't be possible
3555  {
3556  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3557  {
3558  // don't call for stub end routes
3560  }
3561  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3562  }
3563  }
3564  }
3565  }
3566  Utilities->CallLogPop(698);
3567  return; // no route on other track or no other track
3568  }
3569  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3570  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3571  {
3572  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3573  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3574  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3575  {
3576  if(RouteElement.GetELinkPos() == EntryPos)
3577  {
3578  Utilities->CallLogPop(699);
3579  return; // right direction
3580  }
3581  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3582  {
3583  Utilities->CallLogPop(700);
3584  return; // right direction (points)
3585  }
3586  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3587  {
3588  Utilities->CallLogPop(701);
3589  return; // right direction (points)
3590  }
3591  else if(RouteElement.GetXLinkPos() == EntryPos)
3592  {
3593  WrongRoute = true;
3594  break; // wrong direction
3595  }
3596  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3597  {
3598  WrongRoute = true;
3599  break; // wrong direction
3600  }
3601  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3602  {
3603  WrongRoute = true;
3604  break; // wrong direction
3605  }
3606  }
3607  }
3608  if(!WrongRoute)
3609  {
3610  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3611  }
3612  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3613  {
3614  // don't call for stub end routes
3616  }
3617  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3618  Utilities->CallLogPop(703);
3619 }
3620 
3621 // ---------------------------------------------------------------------------
3622 
3623 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3624 {
3625  if(BackgroundColour == NewBackgroundColour)
3626  {
3627  return; // don't replot if already correct
3628 
3629  }
3630  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3631  bool ColourError = false, ColourError2 = false;
3632 
3633  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3634  if(ColourError)
3635  {
3636  ColourError2 = true;
3637  }
3638  for(int x = 0; x < 4; x++)
3639  {
3640  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3641  if(ColourError)
3642  {
3643  ColourError2 = true;
3644  }
3645  }
3646  if(ColourError2)
3647  {
3649  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3650  }
3651  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3652  // of motion
3653  for(int x = 0; x < 4; x++)
3654  {
3655  PlotTrainGraphic(6, x, Disp);
3656  }
3657  BackgroundColour = NewBackgroundColour;
3658  Display->Update();
3659  // need to keep this since Update() not called for PlotSmallOutput as too slow
3660  Utilities->CallLogPop(704);
3661 }
3662 
3663 // ---------------------------------------------------------------------------
3664 
3665 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3666 /*
3667 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3668 
3669 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3670 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3671 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3672 full-element moves.
3673 
3674 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3675 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3676 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3677 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3678 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3679 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3680 In this case set the brake rate to maximum to stop as soon as possible.
3681 
3682 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3683 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3684 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3685 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3686 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3687 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3688 
3689 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3690 first to see whether buffers or continuation) in turn is examined: first the length of the
3691 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3692 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3693 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3694 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3695 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3696 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3697 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3698 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3699 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3700 siding then again emeregency braking may be necessary and a crash may result.
3701 
3702 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3703 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3704 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3705 buffer, then the train accelerates for half the element and brakes for the other half.
3706 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3707 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3708 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3709 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3710 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3711 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3712 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3713 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3714 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3715 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3716 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3717 
3718 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3719 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3720 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3721 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3722 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3723 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3724 MaxBrakeRate.
3725 
3726 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3727 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3728 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3729 
3730 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3731 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3732 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3733 
3734 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3735 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3736 when Straddle == LeadMidLag
3737 */
3738 {
3739  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3740  AnsiString(EntryPos) + "," + HeadCode);
3741  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3742  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3743  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3744  TrainInFrontInSignallerModeFlag = false;
3745  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3746  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3747  bool SignallerStopRequired = false;
3748 
3750  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3751 
3752  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3753 
3754  OneLengthAccelDecel = false;
3755  BrakeRate = 0;
3756 
3757 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3758  if(CurrentTrackVectorPosition > -1)
3759  {
3760  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3761  {
3762  if((EntryPos == 0) || (EntryPos == 2))
3763  {
3764  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3765  {
3766  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3767  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3768  }
3769  else
3770  {
3771  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3772  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3773  }
3774  }
3775  else if(EntryPos == 1)
3776  {
3777  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3778  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3779  }
3780  else // == 3
3781  {
3782  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3783  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3784  }
3785  }
3786  else
3787  {
3788  if(EntryPos > 1)
3789  {
3790  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3791  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3792  }
3793  else
3794  {
3795  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3796  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3797  }
3798  }
3799  EntryHalfLength = CurrentElementHalfLength;
3800  FrontElementLength = 2 * CurrentElementHalfLength;
3801  }
3802  else
3803  {
3804  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3805  }
3806  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3807  {
3808  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3809  }
3810  // check if zero entry speed with another train directly in front & if so remain stopped
3811  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3812  {
3813  EntrySpeed = 0;
3814  ExitSpeedHalf = 0;
3815  ExitSpeedFull = 0;
3816  MaxExitSpeed = 0;
3817  BrakeRate = 0;
3818  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3819  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3820  StoppedForTrainInFront = true;
3821  Utilities->CallLogPop(705);
3822  return;
3823  }
3824  // new at v2.4.0 - check for stopped and zero power
3825  if((EntrySpeed < 1) && PowerAtRail < 1)
3826  {
3827  EntrySpeed = 0;
3828  ExitSpeedHalf = 0;
3829  ExitSpeedFull = 0;
3830  MaxExitSpeed = 0;
3831  BrakeRate = 0;
3832  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3833  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3834  StoppedWithoutPower = true;
3835  Utilities->CallLogPop(2125);
3836  return;
3837  }
3838 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3839  if(BeingCalledOn)
3840  {
3841  LimitingSpeed = CallOnMaxSpeed;
3842  }
3843  else
3844  {
3845  LimitingSpeed = MaximumSpeedLimit;
3846  }
3847  if(LimitingSpeed > FrontElementSpeedLimit)
3848  {
3849  LimitingSpeed = FrontElementSpeedLimit;
3850  }
3851  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3852  {
3853  LimitingSpeed = MaxRunningSpeed;
3854  }
3855  FrontElementMaxSpeed = LimitingSpeed;
3856 
3857 /*
3858  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3859  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3860  (2) V/3.6 = U/3.6 - FT;
3861  (3) S = UT/3.6 - 0.5FT^2
3862  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3863 
3864  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3865  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3866  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3867  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3868  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3869 
3870  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3871  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3872  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3873 */
3874 
3875 // check if running past a red signal without permission
3876  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3877  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3878  {
3879  SPADFlag = true; // user has to intervene to reset & restart after spad
3880  }
3881  if(!SPADFlag)
3882  {
3883  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3884  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3885  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3886  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3887  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3888  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3889 
3890  double ExitSpeedAtMaxBraking;
3891  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3892  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3894  {
3895  ExitSpeedAtMaxBraking = 0;
3896  }
3897  else
3898  {
3899  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3900  }
3901  double SpeedToUse;
3902  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3903  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3904  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3905  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3906  {
3907  SpeedToUse = ExitSpeedAtMaxBraking;
3908  }
3909  else
3910  {
3911  SpeedToUse = LimitingSpeed;
3912  }
3913  if(ExitSpeedFull > SpeedToUse)
3914  {
3915  ExitSpeedFull = SpeedToUse;
3916  }
3917  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3918  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3919 
3920  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3921  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3922  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3923 
3924  do
3925  {
3926  RedSignalFlag = false;
3927  BuffersFlag = false;
3928  StationFlag = false;
3929  BuffersOrContinuationNowFlag = false;
3930  ContinuationNextFlag = false;
3931  // have to reset this after the above test
3932  // add current element length to CumulativeLength
3933  CumulativeLength += (2 * CurrentElementHalfLength);
3934  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3935  {
3936  SignallerStopRequired = true;
3937  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3938  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3939  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3940  if(SignallerStopBrakeRate < TempBR)
3941  {
3942  SignallerStopBrakeRate = TempBR;
3943  }
3944  }
3945  // first check for stops within the length of the current element, where don't want any more checks & don't want
3946  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3947  // during the last loop when the NextTrackVectorPosition was the signal.
3948 
3949  // check if current element is a buffer
3950  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3951  {
3952  // no need to add in the length of this element to CumulativeLength as already included
3953  BuffersFlag = true;
3954  }
3955  // check if current element is a station stop
3956  if(TrainMode == Timetable)
3957  {
3958  bool StopRequired = false;
3959  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3960  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3961  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3962  {
3963  // no need to add in the length of element to CumulativeLength
3964  if(StopRequired)
3965  {
3966  StationFlag = true;
3967  }
3968  }
3969  }
3970  else
3971  {
3972  StationFlag = false;
3973  }
3974  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3975  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3976  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3977  {
3978  BuffersOrContinuationNowFlag = true;
3979  }
3980  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3981  {
3982  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3983  {
3984  if((EntryPos == 0) || (EntryPos == 2))
3985  {
3986  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3987  {
3988  ExitPos = 1;
3989  }
3990  else
3991  {
3992  ExitPos = 3;
3993  }
3994  }
3995  else
3996  {
3997  ExitPos = 0;
3998  }
3999  }
4000  else
4001  {
4002  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4003  }
4004  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
4005  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4006  if(NextTrackVectorPosition > -1)
4007  {
4008  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
4009  // this test & section added at v0.6
4010  {
4011  if((NextEntryPos == 0) || (NextEntryPos == 2))
4012  {
4013  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
4014  {
4015  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
4016  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
4017  }
4018  else
4019  {
4020  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
4021  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
4022  }
4023  }
4024  else if(NextEntryPos == 1)
4025  {
4026  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
4027  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
4028  }
4029  else // == 3
4030  {
4031  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
4032  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
4033  }
4034  }
4035  else
4036  {
4037  if(NextEntryPos > 1)
4038  {
4039  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
4040  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
4041  }
4042  else
4043  {
4044  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4045  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4046  }
4047  }
4048  }
4049  else
4050  {
4051  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4052  }
4053  // now check for stops, first cover those where don't want to add in length of next element
4054  // check if next element is a red signal - Attr 0,
4055  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4056  // CurrentTrackVectorPosition not NextTrackVectorPosition
4057  bool StopRequired;
4058  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4059  {
4060  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4061  {
4062  // no need to add in the length of element to CumulativeLength
4063  RedSignalFlag = true;
4064  }
4065  // next element is a red signal
4066  }
4067  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4068  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4069  // at least one platform element free
4071  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
4072  NextEntryPos, TrainID))
4073  {
4074  // no need to add in the length of element to CumulativeLength
4075  if(StopRequired)
4076  {
4077  StationFlag = true;
4078  }
4079  }
4080  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4081  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4082  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4083  {
4084  // no need to add in the length of element to CumulativeLength
4085  TrainInFrontInSignallerModeFlag = true;
4086  }
4087  // check if next element is a buffer
4088  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
4089  {
4090  // need to add in the length of that element to CumulativeLength
4091  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4092  BuffersFlag = true;
4093  }
4094  // check if next element is a station stop
4096  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4097  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4098  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
4099  {
4100  // need to add in the length of that element to CumulativeLength if a stop required
4101  if(StopRequired)
4102  {
4103  StationFlag = true;
4104  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4105  }
4106  }
4107  }
4108  //now can decide whether need to stop over CumulativeLength
4109  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4110  {
4111  // have to come to a stop over CumulativeLength
4112  if(CumulativeLength == FrontElementLength)
4113  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4114  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4115  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4116  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4117  // if not calc speed at halfway point & if less than above set half speed to this value;
4118  // use constant acceleration in calculating half time point
4119  {
4120  MaxExitSpeed = 0;
4121  double MaxHalfSpeed;
4122  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4123  // have to halve the element length, & can't be zero or negative so no need to test
4124  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4125  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4126  {
4127  MaxHalfSpeed = FrontElementMaxSpeed;
4128  }
4129  else
4130  {
4131  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4132  }
4133  if(MaxHalfSpeed > (2 * EntrySpeed))
4134  // use 2x to prevent kangarooing at last element when had
4135  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4136  {
4137  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4138  0.333334);
4139  bool HalfSpeedLimited = false;
4140  if(MaxHalfSpeed < ExitSpeedHalf)
4141  {
4142  ExitSpeedHalf = MaxHalfSpeed;
4143  HalfSpeedLimited = true;
4144  }
4145  if(PowerAtRail > 1)
4146  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4147  {
4148  // [km/h/3.6 = m/s]
4149  ExitTimeHalf =
4150  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4151  }
4152  else
4153  {
4154  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4155  }
4156  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4157  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4158  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4159  // by a long braking period
4160  ExitSpeedFull = 0;
4161  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4162  if(TempBrakeRate > MaxBrakeRate)
4163  {
4164  TempBrakeRate = MaxBrakeRate;
4165  }
4166  // shouldn't be but leave in anyway
4167  if(TempBrakeRate > BrakeRate)
4168  {
4169  BrakeRate = TempBrakeRate;
4170  }
4171  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4172  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4173  if(HalfSpeedLimited)
4174  // this is the change referred to above
4175  {
4176  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4177  ExitTimeHalf = EntryTime + BrakingTime;
4178  ExitTimeFull = ExitTimeHalf + BrakingTime;
4179  }
4180  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4181  Utilities->CallLogPop(1095);
4182  return;
4183  }
4184  }
4185  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4186  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4187  // calc th, tf, sh, & sf
4188  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4189  if(TempBrakeRate > MaxBrakeRate)
4190  {
4191  TempBrakeRate = MaxBrakeRate;
4192  }
4193  if(TempBrakeRate > BrakeRate)
4194  {
4195  BrakeRate = TempBrakeRate;
4196  }
4197  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4198  if(SignallerStopRequired)
4199  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4200  {
4202  {
4204  // this prevents the brakerate from reducing for a signaller stop
4205  // regardless of other conditions that may change as progress round the loop
4206  }
4207  }
4209  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4210  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4211  {
4213  }
4214  int TempMaxExitSpeed;
4215  // calc current value & if less than MaxExitSpeed set that to this
4216  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4217  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4218  {
4219  MaxExitSpeedAtHalfBraking = 0;
4220  }
4221  else
4222  {
4223  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4224  }
4225  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4226  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4227  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4228  {
4229  TempMaxExitSpeed = FrontElementMaxSpeed;
4230  }
4231  else
4232  {
4233  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4234  }
4235  if(TempMaxExitSpeed < MaxExitSpeed)
4236  {
4237  MaxExitSpeed = TempMaxExitSpeed;
4238  }
4239  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4240  // Cumulativelength, and Cumulativelength
4241 
4242  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4243  {
4244  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4245  if(ExitSpeedHalfSquared < 10)
4246  {
4247  ExitSpeedHalf = 0;
4248  }
4249  else
4250  {
4251  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4252  }
4253  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4254  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4255  if(ExitSpeedFullSquared < 10)
4256  {
4257  ExitSpeedFull = 0;
4258  }
4259  else
4260  {
4261  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4262  }
4263  if((StationFlag) && (CumulativeLength == FrontElementLength))
4264  {
4265  ExitSpeedFull = 0;
4266  // force a stop for station (not for buffers or red signal)
4267  }
4268  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4269  }
4270  // new condition at v2.4.0
4271  else if(PowerAtRail <= 1)
4272  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4273  // avoid using AValue in denominator or have excessively long times
4274  {
4275  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4276  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4277  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4278 
4279  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4280  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4281  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4282  }
4283  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4284  // without the power need above condition or have hours of delay times, above added at v2.4.0
4285  {
4286  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4287  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4288  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4289  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4290  BrakeRate = 0;
4291  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4292  0.333334);
4293  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4294  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4296  // can accelerate continually over the half length
4297  {
4298  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4299  / 86400.0);
4301  // can accelerate continually over the full length
4302  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4303  {
4304  ExitTimeFull =
4305  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4306  }
4307  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4308  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4309  {
4310  // added at v0.6 as a safeguard
4311  if(MaxExitSpeed < EntrySpeed)
4312  {
4314  }
4315  // to prevent DeltaExitTimeToMaxInSecs being negative
4316  if(MaxExitSpeed < 1)
4317  {
4318  MaxExitSpeed = 1;
4319  }
4320  // to prevent divide by zero error
4321  // below as was
4323  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4324  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4325  (1.5 * AValue * AValue);
4326  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4327  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4328  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4329  }
4330  }
4331  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4332  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4333  // second halves of the element
4334  {
4335  // added at v0.6 as a safeguard
4336  if(MaxExitSpeed < EntrySpeed)
4337  {
4339  }
4340  // to prevent DeltaExitTimeToMaxInSecs being negative
4341  if(MaxExitSpeed < 1)
4342  {
4343  MaxExitSpeed = 1; // to prevent divide by zero error
4344  }
4345  // below as was
4347  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4348  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4349  (1.5 * AValue * AValue);
4350  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4351  // remaining distance to half length
4352  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4353  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4355  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4356  }
4357  }
4358  Utilities->CallLogPop(706);
4359  return;
4360  }
4361  else
4362  {
4363  if(!BuffersOrContinuationNowFlag)
4364  {
4365  if(NextSpeedLimit < LimitingSpeed)
4366  {
4367  LimitingSpeed = NextSpeedLimit;
4368  }
4369  }
4370  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4371  int TempMaxExitSpeed;
4372  // calc current value & if less than MaxExitSpeed set that to this
4373  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4374  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4375  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4376  {
4377  MaxExitSpeedAtHalfBraking = 0;
4378  }
4379  else
4380  {
4381  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4382  }
4383  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4384  {
4385  TempMaxExitSpeed = FrontElementMaxSpeed;
4386  }
4387  else
4388  {
4389  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4390  }
4391  if(TempMaxExitSpeed < MaxExitSpeed)
4392  {
4393  MaxExitSpeed = TempMaxExitSpeed;
4394  }
4395  // MaxExitSpeed is an external variable & this can reduce its value
4396  if(EntrySpeed > LimitingSpeed)
4397  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4398  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4399  {
4400  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4401  if(TempBrakeRate > MaxBrakeRate)
4402  {
4403  TempBrakeRate = MaxBrakeRate;
4404  }
4405  // shouldn't be for speedlimits since all known in advance, but include anyway
4406  if(TempBrakeRate > BrakeRate)
4407  {
4408  BrakeRate = TempBrakeRate;
4409  }
4410  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4411  }
4412  }
4413  if(!BuffersOrContinuationNowFlag)
4414  {
4415  CurrentTrackVectorPosition = NextTrackVectorPosition;
4416  EntryPos = NextEntryPos;
4417  CurrentElementHalfLength = NextElementHalfLength;
4418  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4419  {
4420  ContinuationNextFlag = true;
4421  }
4422  }
4423  }
4424  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4426  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4427  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4428  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4429  // stopping distance after it.
4430 
4431  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4432  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4433 
4434  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4435  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4436  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4437  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4438  // too late
4439 
4440  // set final braking or acc'n speed & time values
4441  if(BrakeRate > 0.01)
4442  {
4443  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4444  if(ExitSpeedHalfSquared < 10)
4445  {
4446  ExitSpeedHalf = 0;
4447  }
4448  else
4449  {
4450  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4451  }
4452  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4453  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4454  if(ExitSpeedFullSquared < 10)
4455  {
4456  ExitSpeedFull = 0;
4457  }
4458  else
4459  {
4460  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4461  }
4462  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4463  }
4464  else
4465  {
4466  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4467  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4468  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4469  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4470 
4471  BrakeRate = 0;
4472  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4473  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4474  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4476  {
4477  if(PowerAtRail > 1)
4478  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4479  {
4480  // [km/h/3.6 = m/s]
4481  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4482  / 86400.0);
4483  }
4484  else
4485  {
4486  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4487  }
4489  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4490  {
4491  if(PowerAtRail > 1)
4492  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4493  {
4494  // [km/h/3.6 = m/s]
4495  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4496  / 86400.0);
4497  }
4498  else
4499  {
4500  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4501  }
4502  }
4503  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4504  {
4505  // added at v0.6 as a safeguard
4506  if(MaxExitSpeed < EntrySpeed)
4507  {
4509  }
4510  // to prevent DeltaExitTimeToMaxInSecs being negative
4511  if(MaxExitSpeed < 1)
4512  {
4513  MaxExitSpeed = 1; // to prevent divide by zero error
4514  }
4515  // below as was
4517  double DeltaExitTimeToMaxInSecs;
4518  double DistanceToMax;
4519  if(PowerAtRail > 1) // added at v2.4.0
4520  {
4521  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4522  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4523  (1.5 * AValue * AValue);
4524  }
4525  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4526  {
4527  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4528  // these not really accurate but will be good enough
4529  DistanceToMax = EntryHalfLength;
4530  }
4531  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4532  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4533  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4534  }
4535  }
4536  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4537  {
4538  // added at v0.6 as a safeguard
4539  if(MaxExitSpeed < EntrySpeed)
4540  {
4542  }
4543  // to prevent DeltaExitTimeToMaxInSecs being negative
4544  if(MaxExitSpeed < 1)
4545  {
4546  MaxExitSpeed = 1; // to prevent divide by zero error
4547  }
4548  // below as was
4550  double DeltaExitTimeToMaxInSecs;
4551  double DistanceToMax;
4552  if(PowerAtRail > 1) // added at v2.4.0
4553  {
4554  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4555  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4556  (1.5 * AValue * AValue);
4557  }
4558  else
4559  {
4560  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4561  // these not really accurate but will be good enough
4562  DistanceToMax = EntryHalfLength / 2;
4563  }
4564  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4565  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4566  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4568  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4569  }
4570  }
4571  }
4572 
4573  else // SPADFlag set
4574  {
4576  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4577  if(ExitSpeedHalfSquared < 10)
4578  {
4579  ExitSpeedHalf = 0;
4580  }
4581  else
4582  {
4583  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4584  }
4585  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4586  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4587  if(ExitSpeedFullSquared < 10)
4588  {
4589  ExitSpeedFull = 0;
4590  }
4591  else
4592  {
4593  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4594  }
4595  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4596 
4597  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4598  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4599  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4600  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4601  // will be the stopping speed.
4602  if(ExitSpeedFull > 0)
4603  {
4604  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4605  {
4606  if((EntryPos == 0) || (EntryPos == 2))
4607  {
4608  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4609  {
4610  ExitPos = 1;
4611  }
4612  else
4613  {
4614  ExitPos = 3;
4615  }
4616  }
4617  else
4618  {
4619  ExitPos = 0;
4620  }
4621  }
4622  else
4623  {
4624  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4625  }
4626  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4627  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4628  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4629  {
4630  int NextElementLength;
4631  if(NextEntryPos > 1)
4632  {
4633  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4634  }
4635  else
4636  {
4637  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4638  }
4639  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4640  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4641  {
4642  ExitSpeedFull = 0;
4643  }
4644  }
4645  }
4646  }
4647  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4648  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4649  {
4650  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4651  if(LeadElement > -1)
4652  {
4654  // don't stop on a continuation either entering or leaving
4655  {
4658  MaxExitSpeed = LimitingSpeed;
4659  BrakeRate = 0;
4660  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4661  // assume length is 50m for ease of calc
4662  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4663  Utilities->CallLogPop(2126);
4664  return;
4665  }
4666  }
4667  else if(MidElement > -1)
4668  {
4670  {
4673  MaxExitSpeed = LimitingSpeed;
4674  BrakeRate = 0;
4675  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4676  // assume length is 50m for ease of calc
4677  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4678  Utilities->CallLogPop(2127);
4679  return;
4680  }
4681  }
4682  else if(LagElement > -1)
4683  {
4685  {
4688  MaxExitSpeed = LimitingSpeed;
4689  BrakeRate = 0;
4690  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4691  // assume length is 50m for ease of calc
4692  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4693  Utilities->CallLogPop(2128);
4694  return;
4695  }
4696  }
4697  if(EntrySpeed > 7.5) // keep going for at least another element
4698  {
4699  ExitSpeedHalf = EntrySpeed - 2.5;
4700  ExitSpeedFull = EntrySpeed - 5;
4701  MaxExitSpeed = LimitingSpeed;
4702  BrakeRate = 0;
4703  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4704  // assume length is 50m for ease of calc
4705  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4706  Utilities->CallLogPop(2129);
4707  return;
4708  }
4709  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4710  {
4711  // will appear to have slowed at steady rate
4712  ExitSpeedHalf = 0;
4713  ExitSpeedFull = 0;
4714  MaxExitSpeed = LimitingSpeed;
4715  BrakeRate = 0;
4716  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4717  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4718  StoppedWithoutPower = true;
4719  Utilities->CallLogPop(2130);
4720  return;
4721  }
4722  }
4723  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4724  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4725  Utilities->CallLogPop(707);
4726 }
4727 // ---------------------------------------------------------------------------
4728 /*
4729  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4730  {
4731  int NextExitPos;
4732  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4733  if(TimetableVector.empty()) return false;
4734  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4735  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4736  {
4737  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4738  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4739  {
4740  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4741  }
4742  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4743  {
4744  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4745  }
4746  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4747  NextElement = TempElement;
4748  }
4749  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4750  if(NextElement.TrackType == Buffers) return true;
4751  return false;//shouldn't reach here but include to prevent compiler return warning
4752  }
4753 */
4754 // ---------------------------------------------------------------------------
4755 
4756 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4757 /*
4758  returns the number by which the train ActionVectorEntryPtr needs
4759  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4760  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4761  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4762  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4763  or pass (false) the location.
4764 */{
4765  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4766  Stop = false;
4767  if(TimetableFinished || (Name == ""))
4768  {
4769  Utilities->CallLogPop(957);
4770  return(-1);
4771  }
4773 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4774 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4775 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4776 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4777 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4778 */
4779  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4780  {
4781  Ptr--;
4782  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4783  {
4784  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4785  {
4786  Utilities->CallLogPop(2444);
4787  return(-1);
4788  }
4789  }
4790  }
4791  // start looking from current pointer position
4792  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4793  {
4794  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4795  {
4796  break;
4797  }
4798  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4799  {
4800  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4801  {
4802  Stop = true;
4803  Utilities->CallLogPop(960);
4804  return (Ptr - ActionVectorEntryPtr);
4805  }
4806  }
4807  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4808  {
4809  Utilities->CallLogPop(1517);
4810  return (Ptr - ActionVectorEntryPtr);
4811  }
4812  }
4813  Utilities->CallLogPop(959);
4814  return(-1); // not found a valid entry
4815 }
4816 
4817 // ---------------------------------------------------------------------------
4818 
4820 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4821  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4822  Ignores the call-on signal.
4823 */{
4824  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4825  int ReturnVal = 0;
4826  int ElementCount = 0;
4827 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikley to need to search this far [10km at min length])
4828  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4829  {
4830  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4831  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4832  }
4833 */
4834  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4835  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4836 
4837  while(true)
4838  {
4839  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4840  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4841  {
4842  ReturnVal = 1;
4843  break;
4844  }
4845  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4846  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4847  {
4848  ReturnVal = 2;
4849  break;
4850  }
4851  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4852  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4853  {
4854  ReturnVal = 3;
4855  break;
4856  }
4857 /* not needed at and after v2.12.0, see above
4858  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4859  {
4860  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4861  // must be a loop - reached same point as examined earlier
4862  {
4863  ReturnVal = 4;
4864  break;
4865  }
4866  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4867  {
4868  ReturnVal = 4;
4869  break;
4870  }
4871  }
4872  else
4873  {
4874  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4875  {
4876  ReturnVal = 4;
4877  break;
4878  }
4879  }
4880  if(EntryPos < 2)
4881  {
4882  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4883  }
4884  else
4885  {
4886  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4887  }
4888 */
4889 
4890  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4891  {
4892  if((EntryPos == 0) || (EntryPos == 2))
4893  {
4894  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4895  {
4896  ExitPos = 1;
4897  }
4898  else
4899  {
4900  ExitPos = 3;
4901  }
4902  }
4903  else
4904  {
4905  ExitPos = 0;
4906  }
4907  }
4908  else
4909  {
4910  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4911  }
4912  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4913  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4914  CurrentTrackVectorPosition = NextTrackVectorPosition;
4915  EntryPos = NextEntryPos;
4916  ElementCount++;
4917  if(ElementCount > 1000)
4918  {
4919  ReturnVal = 4;
4920  break;
4921  }
4922  }
4923  if(ReturnVal == 1)
4924  {
4925  Utilities->CallLogPop(708);
4926  return(false);
4927  }
4928  if(ReturnVal == 2)
4929  {
4930  Utilities->CallLogPop(709);
4931  return(true);
4932  }
4933  if(ReturnVal == 3)
4934  {
4935  Utilities->CallLogPop(946);
4936  return(true);
4937  }
4938  if(ReturnVal == 4)
4939  {
4940  Utilities->CallLogPop(947);
4941  return(true);
4942  }
4943  else
4944  {
4945  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4946  }
4947 }
4948 
4949 // ---------------------------------------------------------------------------
4950 
4952 /*
4953  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4954  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4955  change of direction (cdt), remaining here (Frh), or under signaller control);
4956  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4957  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4958  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4959  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4960  m) not failed or stopped without power
4961  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4962  change points outside the route or have a route conflict if another route is set.
4963 */{
4964  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4965  {
4966  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4967  }
4968  // some of the callingon route elements may be involved
4969  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4970  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4971  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4972  int RouteStartPosition;
4973  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4974  int PlatformPosition;
4975  // the track vector position of the first stop platfrom
4976  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4977  // not used here
4978  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4979  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4980  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4981  // must be stopped at a signal but not at a location & still in timetable (a)
4983  // no need to check for SignallerStopped as this function only called in Timetable mode
4984  {
4985  Utilities->CallLogPop(711);
4986  return(false);
4987  }
4988  while(true)
4989  {
4990  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4991  // don't look further than 4km ahead (j)
4992  if(Distance > (4000 + LeadElementDistance))
4993  {
4994  Utilities->CallLogPop(967);
4995  return(false);
4996  }
4997  // if find another train on an element in front, before find a valid platform, return false (c)
4998  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4999  {
5000  Utilities->CallLogPop(713);
5001  return(false);
5002  }
5003  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
5004  // be facing later on)
5005  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
5006  {
5007  // get LeadElement, if -1 return (could be exiting at continuation) (i)
5008  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
5009  if(OtherTrain.LeadElement == -1)
5010  {
5011  Utilities->CallLogPop(714);
5012  return(false);
5013  }
5014  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
5015  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
5016  {
5017  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
5018  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
5019  (OtherTrain.TrainMode == Signaller))
5020  {
5021  break;
5022  }
5023  else
5024  {
5025  Utilities->CallLogPop(955);
5026  return(false);
5027  }
5028  }
5029  else // (h)
5030  {
5031  break;
5032  }
5033  }
5034  // if reach buffers or exit continuation return false (can set route)
5035  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
5036  {
5037  Utilities->CallLogPop(716);
5038  return(false);
5039  }
5040  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
5041  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5043  {
5044  Utilities->CallLogPop(717);
5045  return(false);
5046  }
5047  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5048 /*
5049  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5050  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5051  {
5052  Utilities->CallLogPop(718);
5053  return false;
5054  }
5055 */
5056  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5057  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5058  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5059  {
5060  if(StopRequired)
5061  {
5062  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5063  {
5064  if(!PlatformFoundFlag)
5065  {
5066  PlatformPosition = CurrentTrackVectorPosition;
5067  }
5068  // ensure this only set once at first valid platform position - the unrestricted route will end here
5069  PlatformFoundFlag = true;
5070  }
5071  }
5072  }
5073  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5074  // train has to be at station but that has to be before the next forward signal
5075 /*
5076  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5077  {
5078  Utilities->CallLogPop(719);
5079  return false;
5080  }
5081 */
5082  // make sure points are followed correctly (d) & set ExitPos
5083  if(CurrentTrackElement.TrackType == Points)
5084  {
5085  if((EntryPos == 0) || (EntryPos == 2))
5086  {
5087  if(CurrentTrackElement.Attribute == 0)
5088  {
5089  ExitPos = 1;
5090  }
5091  else
5092  {
5093  ExitPos = 3;
5094  }
5095  }
5096  if(EntryPos == 1)
5097  {
5098  if(CurrentTrackElement.Attribute == 0)
5099  {
5100  ExitPos = 0;
5101  }
5102  else
5103  {
5104  Utilities->CallLogPop(720);
5105  return(false);
5106  }
5107  }
5108  if(EntryPos == 3)
5109  {
5110  if(CurrentTrackElement.Attribute == 1)
5111  {
5112  ExitPos = 0;
5113  }
5114  else
5115  {
5116  Utilities->CallLogPop(721);
5117  return(false);
5118  }
5119  }
5120  }
5121  else
5122  {
5123  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5124  }
5125  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5126  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5127  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5128  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5129  if(ElementNumber < 2)
5130  {
5131  SkipRouteCheck = true;
5132  }
5133  else
5134  {
5135  SkipRouteCheck = false;
5136  }
5137  if(ElementNumber == 1) // the stop signal
5138  {
5139  RouteStartPosition = CurrentTrackVectorPosition;
5140  }
5141 /*
5142  if(ElementNumber == 2)
5143  {
5144  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5145  else AutoSigs = false;
5146  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5147  }
5148 */
5149  if(ElementNumber > 1)
5150  {
5151  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5152  {
5153  RouteOrPartRouteSet = true;
5154  }
5155  else
5156  {
5157  RouteOrPartRouteSet = false;
5158  }
5159  }
5160  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5161  {
5162  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5163  {
5164  Utilities->CallLogPop(1859);
5165  return(false);
5166  }
5167  int ExitLink = CurrentTrackElement.Link[ExitPos];
5168  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5169  {
5170  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5171  {
5172  Utilities->CallLogPop(1850);
5173  return(false);
5174  }
5175  }
5176  }
5177  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5178  if(EntryPos < 2)
5179  {
5180  Distance += CurrentTrackElement.Length01;
5181  }
5182  else
5183  {
5184  Distance += CurrentTrackElement.Length23;
5185  }
5186  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5187  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5188  CurrentTrackVectorPosition = NextTrackVectorPosition;
5189  EntryPos = NextEntryPos;
5190  ElementNumber++;
5191  } // while(true)
5192 
5193  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5194  // from the stop signal (note that it may be last in an autosigs route)
5195  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5196  // this in ClockTimer2)
5197 
5198  // now add elements to the CallonVector
5199  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5200 
5201  AllRoutes->CallonVector.push_back(CallonEntry);
5202  Utilities->CallLogPop(1860);
5203  return(true); // return false if fail to set route for any reason
5204 }
5205 
5206 // ---------------------------------------------------------------------------
5207 /*
5208  bool TTrain::TimetableFinished()
5209  {
5210  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5211  {
5212  return true;
5213  }
5214  return false;
5215  }
5216 */
5217 // ---------------------------------------------------------------------------
5218 
5219 AnsiString TTrain::GetTrainHeadCode(int Caller)
5220 
5221 {
5222  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5223  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5224 
5225  Utilities->CallLogPop(1452);
5226  return(RepeatHeadCode);
5227 }
5228 
5229 // ---------------------------------------------------------------------------
5230 
5231 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5232 {
5233  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5234  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5235 
5236  Utilities->CallLogPop(1453);
5237  return(RepeatTime);
5238 }
5239 
5240 // ---------------------------------------------------------------------------
5241 
5242 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5243 {
5244  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5246  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5247  // first check that train is fully on the railway
5248  bool FrontValid = false, RearValid = false;
5249  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5250 
5251  if((LeadElement == -1) || (MidElement == -1))
5252  {
5253  TrainToBeJoinedBy = NULL;
5254  Utilities->CallLogPop(2131);
5255  return(false);
5256  }
5258  {
5259  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5260  FrontValid = true;
5261  }
5263  {
5264  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5265  RearValid = true;
5266  }
5267  int TrainToBeJoinedByID = -1;
5268 
5269  // first check if on a 2-track element & select correct ID number if so
5270  if(FrontValid)
5271  {
5272  if(FrontAdjacentTrackElement.TrackType == Bridge)
5273  {
5275  {
5276  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5277  }
5278  else
5279  {
5280  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5281  }
5282  }
5283  else
5284  {
5285  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5286  }
5287  }
5288  if((TrainToBeJoinedByID < 0) && RearValid)
5289  {
5290  // first check if on a 2-track element & select correct ID number if so
5291  if(RearAdjacentTrackElement.TrackType == Bridge)
5292  {
5294  {
5295  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5296  }
5297  else
5298  {
5299  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5300  }
5301  }
5302  else
5303  {
5304  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5305  }
5306  }
5307  if(TrainToBeJoinedByID < 0) // no adjacent train
5308  {
5309  TrainToBeJoinedBy = NULL;
5310  Utilities->CallLogPop(2132);
5311  return(false);
5312  }
5313  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5314  if(!TrainToBeJoinedBy->Stopped())
5315  {
5316  TrainToBeJoinedBy = NULL;
5317  Utilities->CallLogPop(2133);
5318  return(false);
5319  }
5320  Utilities->CallLogPop(2134);
5321  return(true);
5322 }
5323 
5324 // ---------------------------------------------------------------------------
5325 
5326 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5327  TDateTime TimetableNonRepeatTime, bool Warning)
5328 /*
5329  Time = timetable time, the time adjustments for repeat trains is carried out internally
5330  Not all messages need this, if not needed a dummy value is required but not used
5331 
5332  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5333  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5334  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5335  //NB for Frh just give terminated message but without event time - don't use this function
5336  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5337  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5338  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5339  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5340  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5341  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5342  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5343  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5344  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5345  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5346  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5347  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5348  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5349  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5350  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5351  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5352  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass stop signal
5353  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5354  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5355  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5356  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5357  SignallerStop 06:05:40: 2F46 stopped on signaller command
5358  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5359  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5360 */{
5361  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5362  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5363  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5364  int IntMinsLate = 0;
5365 
5366  // need to set it in case MinsLate == 0, since it isn't tested for that
5367  if(ActionType == Arrive)
5368  {
5369  ActionLog = " arrived at ";
5370  }
5371  if(ActionType == Terminate)
5372  {
5373  if(TerminatedMessageSent) // to avoid it being sent twice
5374  {
5375  Utilities->CallLogPop(1104);
5376  return;
5377  }
5378  ActionLog = " terminated at ";
5379  TerminatedMessageSent = true;
5380  }
5381  if(ActionType == Depart)
5382  {
5383  ActionLog = " departed from ";
5384  }
5385  if(ActionType == Pass)
5386  {
5387  ActionLog = " passed ";
5388  }
5389  if(ActionType == Create)
5390  {
5391  ActionLog = " created at ";
5392  }
5393  if(ActionType == Enter)
5394  {
5395  ActionLog = " entered railway at ";
5396  }
5397  if(ActionType == Leave)
5398  {
5399  ActionLog = " left railway at ";
5400  }
5401  if(ActionType == FrontSplit)
5402  {
5403  ActionLog = " split from front to ";
5404  }
5405  if(ActionType == RearSplit)
5406  {
5407  ActionLog = " split from rear to ";
5408  }
5409  if(ActionType == JoinedByOther)
5410  {
5411  ActionLog = " joined by ";
5412  }
5413  if(ActionType == ChangeDirection)
5414  {
5415  ActionLog = " changed direction at ";
5416  }
5417  if(ActionType == NewService)
5418  {
5419  ActionLog = " became new service ";
5420  }
5421  if(ActionType == TakeSignallerControl)
5422  {
5423  ActionLog = " taken under signaller control at ";
5424  }
5425  if(ActionType == RestoreTimetableControl)
5426  {
5427  ActionLog = " restored to timetable control at ";
5428  }
5429  if(ActionType == RemoveTrain)
5430  {
5431  if(Crashed)
5432  {
5433  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5434  }
5435  else if(Derailed)
5436  {
5437  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5438  }
5439  else
5440  {
5441  ActionLog = " REMOVED FROM RAILWAY at ";
5442  }
5443  }
5444  if(ActionType == SignallerMoveForwards)
5445  {
5446  ActionLog = " received signaller authority to proceed";
5447  }
5448  if(ActionType == SignallerStepForward)
5449  {
5450  ActionLog = " received signaller authority to step forward";
5451  }
5452  if(ActionType == SignallerChangeDirection)
5453  {
5454  ActionLog = " changed direction under signaller control at ";
5455  }
5456  if(ActionType == SignallerPassRedSignal)
5457  {
5458  ActionLog = " received signaller authority to pass stop signal";
5459  }
5460  if(ActionType == SignallerControlStop)
5461  {
5462  ActionLog = " received signaller instruction to stop";
5463  }
5464  if(ActionType == SignallerStop)
5465  {
5466  ActionLog = " stopped on signaller instruction ";
5467  }
5468  if(ActionType == SignallerJoin)
5469  {
5470  ActionLog = " joined under signaller control by ";
5471  }
5472  if(ActionType == TrainFailure)
5473  {
5474  ActionLog = " suffered an onboard power failure at ";
5475  }
5476  if(ActionType == RepairFailedTrain)
5477  {
5478  ActionLog = " failure repaired at ";
5479  }
5480  if(ActionType == SignallerLeave)
5481  {
5482  ActionLog = " left railway under signaller control at ";
5483  }
5484  if(OtherHeadCode != "")
5485  {
5486  OtherHeadCode += " at ";
5487  }
5488  TDateTime ActualTime = TrainController->TTClockTime;
5489 
5490  if(Warning)
5491  {
5492  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5493  WarningBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5494  }
5495  else
5496  {
5497  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5498  }
5499  bool TimePerformance = true;
5500 
5501  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5502  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5503  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5504  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5505  // SignallerJoin & RepairFailedTrain new at v2.4.0
5506  {
5507  TimePerformance = false;
5508  }
5509  if(TimePerformance)
5510  {
5511  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5512  MinsDelayed = float(MinsLate);
5513  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5514  {
5515  MinsDelayed = 0;
5516  }
5517  // new v2.2.0 for OpActionPanel, can be positive or negative
5518  if(ActionType == Arrive)
5519  {
5521  }
5522  // since train has just arrived this value is the
5523  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5524  // subtracted from later stop recoverable times.
5525  if(MinsLate < 0)
5526  {
5527  IntMinsLate = int(ceil(MinsLate));
5528  }
5529  if(MinsLate > 0)
5530  {
5531  IntMinsLate = int(floor(MinsLate));
5532  }
5533  if(IntMinsLate == 0)
5534  {
5535  PerfLog = " on time";
5536  }
5537  else if(IntMinsLate == 1)
5538  {
5539  PerfLog = " 1 minute late";
5540  }
5541  else if(IntMinsLate == -1)
5542  {
5543  PerfLog = " 1 minute early";
5544  }
5545  else if(IntMinsLate > 1)
5546  {
5547  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5548  }
5549  else if(IntMinsLate < -1)
5550  {
5551  int PosIntMinsLate = -IntMinsLate;
5552  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5553  }
5554  if(LocationName.Pos('-') > 0)
5555  {
5556  PerfLog = "," + PerfLog;
5557  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5558  }
5559  PerfLogForm->PerformanceLog(0, BaseLog + PerfLog);
5560  }
5561  else
5562  {
5563  PerfLogForm->PerformanceLog(1, BaseLog);
5564  }
5565  if(Warning)
5566  {
5567  Display->WarningLog(0, WarningBaseLog);
5568  }
5569  // update statistics
5570  if((ActionType == Arrive) && (IntMinsLate == 0))
5571  {
5573  }
5574  else if((ActionType == Arrive) && (IntMinsLate > 0))
5575  {
5577  TrainController->TotLateArrMins += IntMinsLate;
5578  }
5579  else if((ActionType == Arrive) && (IntMinsLate < 0))
5580  {
5582  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5583  }
5584 
5585  else if((ActionType == Pass) && (IntMinsLate == 0))
5586  {
5588  }
5589  else if((ActionType == Pass) && (IntMinsLate > 0))
5590  {
5592  TrainController->TotLatePassMins += IntMinsLate;
5593  }
5594  else if((ActionType == Pass) && (IntMinsLate < 0))
5595  {
5597  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5598  }
5599 
5600  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5601  {
5603  }
5604  else if((ActionType == Leave) && (IntMinsLate > 0))
5605  {
5607  TrainController->TotLateExitMins += IntMinsLate;
5608  }
5609  else if((ActionType == Leave) && (IntMinsLate < 0))
5610  {
5612  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5613  }
5614 
5615  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5616  {
5618  }
5619  else if((ActionType == Depart) && (IntMinsLate > 0))
5620  {
5622  TrainController->TotLateDepMins += IntMinsLate;
5623  }
5624  Utilities->CallLogPop(968);
5625 }
5626 
5627 // ---------------------------------------------------------------------------
5628 
5629 void TTrain::TrainHasFailed(int Caller)
5630 {
5631  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5632  if(Crashed || Derailed || DerailPending)
5633  {
5634  TrainFailurePending = false;
5635  Utilities->CallLogPop(2135);
5636  return;
5637  }
5638  AnsiString LocName = "";
5639 
5640  if(LeadElement > -1)
5641  {
5643  }
5644  if((LocName == "") && (MidElement > -1))
5645  {
5647  }
5648  if((LocName == "") && LeadElement > -1)
5649  {
5650  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5651  }
5652  if((LocName == "") && (MidElement > -1))
5653  {
5654  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5655  }
5656  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5657  TrainFailed = true;
5658  TrainFailurePending = false;
5659  CallingOnFlag = false; //added at v2.10.0
5661  PowerAtRail = 0.08;
5662  AValue = sqrt(2 * PowerAtRail / Mass);
5664  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5665  if(Stopped())
5666  {
5667  EntrySpeed = 0;
5668  ExitSpeedHalf = 0;
5669  ExitSpeedFull = 0;
5670  MaxExitSpeed = 0;
5671  BrakeRate = 0;
5672  StoppedWithoutPower = true;
5673  }
5675  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5676  // true for warning, TDateTime not used
5677  Utilities->CallLogPop(2136);
5678 }
5679 
5680 // ---------------------------------------------------------------------------
5681 
5682 void TTrain::FrontTrainSplit(int Caller)
5683 {
5684 /*
5685  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5686  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5687  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5688  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5689 */
5690  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5691  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5692  if(PowerAtRail < 1)
5693  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5694  {
5696  {
5697  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5698  }
5700  Utilities->CallLogPop(2137);
5701  return;
5702  }
5703  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5704 
5705  if(LocationName == "")
5706  {
5707  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5708  }
5709  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5710  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5711  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5713 
5714  // determine all positions & exits
5715  if(LocationName != "")
5716  {
5717  // if message given only call at ~5 sec intervals
5719  {
5720  FirstNamedElementPos = LeadElement;
5721  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5722  // check if possible with LeadElement as First
5723  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5724  {
5725  FirstNamedElementPos = MidElement;
5726  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5727  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5728  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5729  {
5731  {
5732  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5734  }
5735  Utilities->CallLogPop(1009);
5736  return;
5737  }
5738  }
5739  else
5740  {
5741  // if first is possible then check if all 4 positions at location, and if not try the second
5742  int LeadPosA = FirstNamedElementPos;
5743  int LeadPosB = FirstNamedLinkedElementPos;
5744  int LeadPosC = SecondNamedElementPos;
5745  int LeadPosD = SecondNamedLinkedElementPos;
5746  // count number of positions that are at the location
5747  int LeadNumAtLoc = 0;
5748  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5749  {
5750  LeadNumAtLoc++;
5751  }
5752  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5753  {
5754  LeadNumAtLoc++;
5755  }
5756  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5757  {
5758  LeadNumAtLoc++;
5759  }
5760  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5761  {
5762  LeadNumAtLoc++;
5763  }
5764  if(LeadNumAtLoc < 4)
5765  {
5766  FirstNamedElementPos = MidElement;
5767  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5768  SecondNamedLinkedElementPos)) // restore originals
5769  {
5770  FirstNamedElementPos = LeadPosA;
5771  FirstNamedLinkedElementPos = LeadPosB;
5772  SecondNamedElementPos = LeadPosC;
5773  SecondNamedLinkedElementPos = LeadPosD;
5774  }
5775  else // count number at location
5776  {
5777  int MidNumAtLoc = 0;
5778  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5779  {
5780  MidNumAtLoc++;
5781  }
5782  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5783  {
5784  MidNumAtLoc++;
5785  }
5786  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5787  {
5788  MidNumAtLoc++;
5789  }
5790  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5791  {
5792  MidNumAtLoc++;
5793  }
5794  if(LeadNumAtLoc > MidNumAtLoc)
5795  // change back, else keep new values
5796  {
5797  FirstNamedElementPos = LeadPosA;
5798  FirstNamedLinkedElementPos = LeadPosB;
5799  SecondNamedElementPos = LeadPosC;
5800  SecondNamedLinkedElementPos = LeadPosD;
5801  }
5802  }
5803  }
5804  }
5805  }
5806  else
5807  {
5808  Utilities->CallLogPop(1791);
5809  return;
5810  }
5811  }
5812  else
5813  {
5814  throw Exception("Error - LocationName not set in FrontTrainSplit");
5815  }
5816  // set front & rear train parameters
5817  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5818  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5819  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5820  if(LeadElement == FirstNamedElementPos)
5821  {
5822  if(MidElement == SecondNamedElementPos)
5823  {
5824  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5825  FrontTrainRearPosition = FirstNamedElementPos;
5826  RearTrainFrontPosition = SecondNamedElementPos;
5827  RearTrainRearPosition = SecondNamedLinkedElementPos;
5828  }
5829  else // MidElement must == FirstNamedLinkedElementPos
5830  {
5831  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5832  FrontTrainRearPosition = SecondNamedElementPos;
5833  RearTrainFrontPosition = FirstNamedElementPos;
5834  RearTrainRearPosition = FirstNamedLinkedElementPos;
5835  }
5836  }
5837  else // MidElement == FirstNamedElementPos
5838  {
5839  if(LeadElement == SecondNamedElementPos)
5840  {
5841  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5842  FrontTrainRearPosition = SecondNamedElementPos;
5843  RearTrainFrontPosition = FirstNamedElementPos;
5844  RearTrainRearPosition = FirstNamedLinkedElementPos;
5845  }
5846  else // LeadElement must == FirstNamedLinkedElementPos
5847  {
5848  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5849  FrontTrainRearPosition = FirstNamedElementPos;
5850  RearTrainFrontPosition = SecondNamedElementPos;
5851  RearTrainRearPosition = SecondNamedLinkedElementPos;
5852  }
5853  }
5854  RearTrainExitPos = -1;
5855  for(int x = 0; x < 4; x++)
5856  {
5857  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5858  {
5859  RearTrainExitPos = x;
5860  break;
5861  }
5862  }
5863  if(RearTrainExitPos == -1)
5864  {
5865  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5866  }
5867  FrontTrainExitPos = -1;
5868  for(int x = 0; x < 4; x++)
5869  {
5870  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5871  {
5872  FrontTrainExitPos = x;
5873  break;
5874  }
5875  }
5876  if(FrontTrainExitPos == -1)
5877  {
5878  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5879  }
5880  // check no train (apart from self) on any of the 4 elements & fail if so
5881  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5882  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5883 
5884  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5885  {
5886  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5887  }
5888  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5889  {
5890  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5891  }
5892  else
5893  {
5894  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5895  }
5896  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5897  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5898  // can't be a bridge
5899  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5900  // can't be a bridge
5901  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5902  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5903 
5904  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5905  {
5906  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5907  }
5908  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5909  {
5910  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5911  }
5912  else
5913  {
5914  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5915  }
5916  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5917  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5918  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5919  {
5921  {
5924  }
5925  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5926  Utilities->CallLogPop(1010);
5927  return;
5928  }
5930  {
5932  }
5933  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5934  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5935  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5936  // variable as it is needed for setting up the new train
5937  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5938 
5939  UnplotTrain(0);
5940  StartSpeed = 0;
5941  RearStartElement = RearTrainRearPosition;
5942  RearStartExitPos = RearTrainExitPos;
5943  StoppedAtLocation = true;
5944  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5945  {
5946  StoppedWithoutPower = true;
5947  }
5948  PlotStartPosition(3);
5953 
5954  Mass = Mass / 2;
5955  MaxBrakeRate = MaxBrakeRate / 2;
5956  PowerAtRail = PowerAtRail / 2;
5957  AValue = sqrt(2 * PowerAtRail / Mass);
5958  // shouldn't change but include in case not set earlier
5959 
5960  // create new front train
5961 /*
5962  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5963  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5964  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5965 */
5966  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5967  TActionEventType EventType = NoEvent;
5968 
5969  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5970  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5971  // false for SignallerControl
5972  {
5973  Utilities->CallLogPop(1721); // EventType not used here
5974  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5975  // another train, in which case a message will have been sent to the perf log, also might well clear later
5976  // when other train moves away
5977  return;
5978  }
5979  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5980  // see mods in UpdateTrain for v1.3.2
5981  TrainController->TrainAdded = true;
5982 
5983  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5984 
5985  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5986  TTOD.RunningEntry = Running;
5987  Utilities->CallLogPop(998);
5988 }
5989 
5990 // ---------------------------------------------------------------------------
5991 
5992 void TTrain::RearTrainSplit(int Caller)
5993 {
5994 /*
5995  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5996  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5997  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5998  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5999 */
6000  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6001  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6002  if(PowerAtRail < 1)
6003  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
6004  {
6006  {
6007  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
6008  }
6010  Utilities->CallLogPop(2138);
6011  return;
6012  }
6013  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
6014 
6015  if(LocationName == "")
6016  {
6017  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
6018  }
6019  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
6020  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
6021  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
6023 
6024  // determine all positions & exits
6025  if(LocationName != "")
6026  {
6027  // if message given only call at ~5 sec intervals
6029  {
6030  FirstNamedElementPos = LeadElement;
6031  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6032  SecondNamedLinkedElementPos))
6033  {
6034  FirstNamedElementPos = MidElement;
6035  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6036  SecondNamedLinkedElementPos))
6037  {
6039  {
6040  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
6042  }
6043  Utilities->CallLogPop(1013);
6044  return;
6045  }
6046  }
6047  else
6048  {
6049  // if first is possible then check if all 4 positions at location, and if not try the second
6050  int LeadPosA = FirstNamedElementPos;
6051  int LeadPosB = FirstNamedLinkedElementPos;
6052  int LeadPosC = SecondNamedElementPos;
6053  int LeadPosD = SecondNamedLinkedElementPos;
6054  // count number of positions that are at the location
6055  int LeadNumAtLoc = 0;
6056  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
6057  {
6058  LeadNumAtLoc++;
6059  }
6060  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
6061  {
6062  LeadNumAtLoc++;
6063  }
6064  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
6065  {
6066  LeadNumAtLoc++;
6067  }
6068  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
6069  {
6070  LeadNumAtLoc++;
6071  }
6072  if(LeadNumAtLoc < 4)
6073  {
6074  FirstNamedElementPos = MidElement;
6075  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6076  SecondNamedLinkedElementPos)) // restore originals
6077  {
6078  FirstNamedElementPos = LeadPosA;
6079  FirstNamedLinkedElementPos = LeadPosB;
6080  SecondNamedElementPos = LeadPosC;
6081  SecondNamedLinkedElementPos = LeadPosD;
6082  }
6083  else // count number at location
6084  {
6085  int MidNumAtLoc = 0;
6086  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
6087  {
6088  MidNumAtLoc++;
6089  }
6090  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6091  {
6092  MidNumAtLoc++;
6093  }
6094  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6095  {
6096  MidNumAtLoc++;
6097  }
6098  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6099  {
6100  MidNumAtLoc++;
6101  }
6102  if(LeadNumAtLoc > MidNumAtLoc)
6103  // change back, else keep new values
6104  {
6105  FirstNamedElementPos = LeadPosA;
6106  FirstNamedLinkedElementPos = LeadPosB;
6107  SecondNamedElementPos = LeadPosC;
6108  SecondNamedLinkedElementPos = LeadPosD;
6109  }
6110  }
6111  }
6112  }
6113  }
6114  else
6115  {
6116  Utilities->CallLogPop(1792);
6117  return;
6118  }
6119  }
6120  else
6121  {
6122  throw Exception("Error - LocationName not set in RearTrainSplit");
6123  }
6124  // set front & rear train parameters
6125  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
6126  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
6127  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
6128  if(LeadElement == FirstNamedElementPos)
6129  {
6130  if(MidElement == SecondNamedElementPos)
6131  {
6132  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6133  FrontTrainRearPosition = FirstNamedElementPos;
6134  RearTrainFrontPosition = SecondNamedElementPos;
6135  RearTrainRearPosition = SecondNamedLinkedElementPos;
6136  }
6137  else // MidElement must == FirstNamedLinkedElementPos
6138  {
6139  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6140  FrontTrainRearPosition = SecondNamedElementPos;
6141  RearTrainFrontPosition = FirstNamedElementPos;
6142  RearTrainRearPosition = FirstNamedLinkedElementPos;
6143  }
6144  }
6145  else // MidElement == FirstNamedElementPos
6146  {
6147  if(LeadElement == SecondNamedElementPos)
6148  {
6149  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6150  FrontTrainRearPosition = SecondNamedElementPos;
6151  RearTrainFrontPosition = FirstNamedElementPos;
6152  RearTrainRearPosition = FirstNamedLinkedElementPos;
6153  }
6154  else // LeadElement must == FirstNamedLinkedElementPos
6155  {
6156  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6157  FrontTrainRearPosition = FirstNamedElementPos;
6158  RearTrainFrontPosition = SecondNamedElementPos;
6159  RearTrainRearPosition = SecondNamedLinkedElementPos;
6160  }
6161  }
6162  RearTrainExitPos = -1;
6163  for(int x = 0; x < 4; x++)
6164  {
6165  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
6166  {
6167  RearTrainExitPos = x;
6168  break;
6169  }
6170  }
6171  if(RearTrainExitPos == -1)
6172  {
6173  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
6174  }
6175  FrontTrainExitPos = -1;
6176  for(int x = 0; x < 4; x++)
6177  {
6178  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
6179  {
6180  FrontTrainExitPos = x;
6181  break;
6182  }
6183  }
6184  if(FrontTrainExitPos == -1)
6185  {
6186  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
6187  }
6188  // check no train (apart from self) on any of the 4 elements & fail if so
6189  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
6190  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
6191 
6192  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
6193  {
6194  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6195  }
6196  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
6197  {
6198  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6199  }
6200  else
6201  {
6202  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
6203  }
6204  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6205  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6206  // can't be a bridge
6207  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6208  // can't be a bridge
6209  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6210  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6211 
6212  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6213  {
6214  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6215  }
6216  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6217  {
6218  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6219  }
6220  else
6221  {
6222  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6223  }
6224  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6225  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6226  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6227  {
6229  {
6232  }
6233  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6234  Utilities->CallLogPop(1014);
6235  return;
6236  }
6238  {
6240  }
6241  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6242  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6243  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6244  // variable as it is needed for setting up the new train
6245  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6246 
6247  UnplotTrain(1);
6248  StartSpeed = 0;
6249  RearStartElement = FrontTrainRearPosition;
6250  RearStartExitPos = FrontTrainExitPos;
6251  StoppedAtLocation = true;
6252  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6253  {
6254  StoppedWithoutPower = true;
6255  }
6256  PlotStartPosition(4);
6261  Mass = Mass / 2;
6262  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6263  MaxBrakeRate = MaxBrakeRate / 2;
6264  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6265  PowerAtRail = PowerAtRail / 2;
6266  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6267  AValue = sqrt(2 * PowerAtRail / Mass);
6268  // shouldn't change but include in case not set earlier
6269 
6270  // create new rear train
6271 /*
6272  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6273  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6274  int RepeatNumber, int IncrementalMinutes)
6275 */
6276  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6277  TActionEventType EventType = NoEvent;
6278 
6279  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6280  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6281  // false for SignallerControl
6282  {
6283  Utilities->CallLogPop(1722); // EventType not used here
6284  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6285  // another train, in which case a message will have been sent to the perf log, also might well clear later
6286  // when other train moves away
6287  return;
6288  }
6289  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6290  // see mods in UpdateTrain for v1.3.2
6291  TrainController->TrainAdded = true;
6292  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6293 
6294  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6295  TTOD.RunningEntry = Running;
6296  Utilities->CallLogPop(1015);
6297 }
6298 
6299 // ---------------------------------------------------------------------------
6300 
6301 void TTrain::FinishJoin(int Caller)
6302 {
6303  if(FinishJoinLogSent == false)
6304  {
6305  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6306  FinishJoinLogSent = true; // so don't keep logging this event
6307  // don't need to reset it to false after the event as the train is deleted
6308  }
6309  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6310  if(TrainFailed)
6311  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6312  {
6314  {
6315  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6316  }
6318  Utilities->CallLogPop(2139);
6319  return;
6320  }
6321  if(TrainGone)
6322  // this means that the train has already joined the other & is awaiting deletion by TrainController
6323  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6324  // on from jbo & TrainToJoinIsAdjacent returns false
6325  {
6326  Utilities->CallLogPop(1035);
6327  return;
6328  }
6329  TTrain *TrainToJoin;
6331 
6332  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6333  {
6335  {
6336  // PerfLogForm->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6339  }
6340  Utilities->CallLogPop(1030);
6341  return; // keep this here in case need to add code before final return
6342  }
6343  // no need to clear error report flag here, cleared in jbo function
6344  // No need to set TimetableFinished, done in jbo function
6345  Utilities->CallLogPop(1031);
6346 }
6347 
6348 // ---------------------------------------------------------------------------
6349 
6350 void TTrain::JoinedBy(int Caller)
6351 {
6352  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6353  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6354  if(PowerAtRail < 1)
6355  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6356  {
6358  {
6359  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6360  }
6362  Utilities->CallLogPop(2140);
6363  return;
6364  }
6365  TTrain *TrainToBeJoinedBy;
6367 
6368  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6369  {
6371  {
6372  // PerfLogForm->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6375  }
6376  LastActionDelayFlag = true;
6377  // need to update LastActionTime if this train first to arrive as need 30s after
6378  // both adjacent before the join
6379  Utilities->CallLogPop(1032);
6380  return;
6381  }
6382  // here when other train is adjacent
6384  {
6386  // need to update this as need 30s after both adjacent before the join
6387  LastActionDelayFlag = false;
6388  Utilities->CallLogPop(1033);
6389  return;
6390  }
6391  // here when other train is adjacent & 30 secs elapsed since both adjacent
6392 
6393  // set new values for mass etc
6394  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6395  {
6396  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6397  }
6398  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6399  double OwnBrakeForce = MaxBrakeRate * Mass;
6400  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6401 
6402  Mass += TrainToBeJoinedBy->Mass;
6403  MaxBrakeRate = CombinedBrakeRate;
6404  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6405  AValue = sqrt(2 * PowerAtRail / Mass);
6406 
6408  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6409  TrainToBeJoinedBy->TimetableFinished = true;
6410  TrainToBeJoinedBy->TrainGone = true;
6411  // this will cause other train to be deleted
6412  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6416  Utilities->CallLogPop(1034);
6417 }
6418 
6419 // ---------------------------------------------------------------------------
6420 
6421 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6422 {
6423  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6424  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6425  if(PowerAtRail < 1)
6426  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6427  {
6429  {
6430  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6431  }
6432  ZeroPowerNoCDTMessage = true;
6433  Utilities->CallLogPop(2141);
6434  return;
6435  }
6436  TColor TempColour = BackgroundColour;
6437 
6438  UnplotTrain(2);
6441  StartSpeed = 0;
6442  StoppedAtLocation = true;
6443  PlotStartPosition(1);
6444  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6445  // plot same as was - should always be pale green
6446  if(!NoLogFlag)
6447  {
6450  }
6452 
6453  //now erase a stub route if there is one, added at v2.5.1
6454  //first element of route is now immediately behind the train (i.e. next to MidElement)
6455  if(MidEntryPos >= 0)
6456  {
6457  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6458  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6459  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6460  int RouteNumber = -1;
6461  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6462  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6463  {
6464  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6465  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6466  //elements can continue to be removed from that route
6467  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6468  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6469  {
6470  bool FirstPass = true; //added at v2.8.0
6471  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6472  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6473  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6474  int TVPos2 = PDE.GetTrackVectorPosition();
6475  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6476  {
6477  break;
6478  }
6479  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6481  {
6482  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6483  }
6484  else
6485  {
6486  break;
6487  }
6488  FirstPass = false;
6489  }
6490  AllRoutes->RebuildRailwayFlag = true;
6491  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6492  }
6493  }
6494  }
6495  Utilities->CallLogPop(1012);
6496 }
6497 
6498 // ---------------------------------------------------------------------------
6499 
6500 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6501 // change to new train, give new service message
6502 //same RepeatNumber used for the new service
6503 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6504  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6505  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6506  if(PowerAtRail < 1)
6507  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6508  {
6510  {
6511  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6512  }
6514  Utilities->CallLogPop(2142);
6515  return;
6516  }
6518 
6519  if(!NoLogFlag)
6520  {
6522  }
6523  UnplotTrain(3);
6526  StartSpeed = 0;
6531  HeadCode = NewHeadCode;
6532  StoppedAtLocation = true;
6533  PlotStartPosition(5);
6535  // pale green
6538  TerminatedMessageSent = false;
6539  Utilities->CallLogPop(1022);
6540 }
6541 
6542 // ---------------------------------------------------------------------------
6543 
6544 void TTrain::RemainHere(int Caller)
6545 {
6546  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6547  if(RemainHereLogNotSent) // to prevent repeated logs
6548  {
6549  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6550  RemainHereLogNotSent = false;
6551  }
6553  {
6557  TerminatedMessageSent = true;
6558  }
6559  TimetableFinished = true;
6560  Utilities->CallLogPop(1023);
6561 }
6562 
6563 // ---------------------------------------------------------------------------
6564 
6565 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6566 /*
6567  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6568  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6569  except where an action is a departure, starting at the current value for the pointer
6570  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6571  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6572 */{
6573  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6574  {
6575  return; // if remove train that starts under signaller control no messages needed
6576 
6577  }
6578  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6579  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6580  if(IncNum > 0)
6581  {
6582  for(int x = 0; x < IncNum; x++)
6583  {
6584  if(x > 0)
6585  {
6586  Ptr++;
6587  }
6588  // arrival - no need to test for termination as this section only covers missed actions up to the
6589  // arrival point - may terminate later but that not missed
6590  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6591  {
6593  }
6594  // arrival & departure
6595  if(Ptr->FormatType == TimeTimeLoc)
6596  {
6598  }
6599  // departure
6600  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6601  {
6602  continue; // skip TimeLoc departures, message given for arrivals
6603  }
6604  // pass
6605  else if(Ptr->FormatType == PassTime)
6606  {
6608  }
6609  // split
6610  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6611  {
6613  }
6614  // jbo
6615  else if(Ptr->Command == "jbo")
6616  {
6618  }
6619  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6620  // be starts, finishes or cdt
6621  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6622  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6623  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6624  (Ptr->FormatType == Repeat))
6625  {
6626  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6627  }
6628  }
6629  }
6630  else
6631  {
6632  bool IncludeFER = false;
6633  if(IncNum == -1)
6634  {
6635  IncludeFER = true;
6636  }
6637  while(true) // finish commands & repeats break out of loop
6638  {
6639  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6640  if(!IncludeFER && (Ptr->Command == "Fer"))
6641  {
6642  break;
6643  }
6644  // Fer & included
6645  else if(IncludeFER && (Ptr->Command == "Fer"))
6646  {
6648  break;
6649  }
6650  // Repeat
6651  else if(Ptr->FormatType == Repeat)
6652  {
6653  break;
6654  }
6655  // Fjo
6656  else if(Ptr->Command == "Fjo")
6657  {
6659  break;
6660  }
6661  // Frh
6662  else if(Ptr->Command == "Frh")
6663  {
6665  {
6667  TerminatedMessageSent = true;
6668  }
6669  break;
6670  }
6671  // Frh-sh
6672  else if(Ptr->Command == "Frh-sh")
6673  {
6675  {
6677  TerminatedMessageSent = true;
6678  }
6679  break;
6680  }
6681  // Fns, F-nshs, Fns-sh
6682  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6683  {
6685  break;
6686  }
6687  // end of breakout actions
6688  // arrival
6689  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6690  {
6691  if(IsTrainTerminating(1))
6692  {
6694  TerminatedMessageSent = true;
6695  }
6696  else
6697  {
6699  }
6700  }
6701  // arrival & departure
6702  else if(Ptr->FormatType == TimeTimeLoc)
6703  {
6705  }
6706  // departure
6707  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6708  {
6709  Ptr++;
6710  continue; // skip TimeLoc departures, message given for arrivals
6711  }
6712  // pass
6713  else if(Ptr->FormatType == PassTime)
6714  {
6716  }
6717  // split
6718  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6719  {
6721  }
6722  // jbo
6723  else if(Ptr->Command == "jbo")
6724  {
6726  }
6727  // cdt
6728  else if(Ptr->Command == "cdt")
6729  {
6730 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6731  }
6732  // Errors
6733  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6734  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6735  {
6736  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6737  }
6738  Ptr++;
6739  }
6740  TimetableFinished = true;
6741  }
6742  Utilities->CallLogPop(1021);
6743 }
6744 
6745 // ---------------------------------------------------------------------------
6746 
6747 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6748 // ensure same repeatnumber
6749 {
6750  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6752 
6753  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6754  {
6755  Utilities->CallLogPop(1024);
6756  return(false);
6757  }
6758  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6759  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6760  {
6761  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6762  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6763  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6764  {
6765  Utilities->CallLogPop(1025);
6766  return(true);
6767  }
6768  }
6769  Utilities->CallLogPop(1026);
6770  return(false);
6771 }
6772 
6773 // ---------------------------------------------------------------------------
6774 
6775 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6776 // ensure same repeatnumber
6777 {
6778  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6779  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6780 
6781  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6782  {
6783  Utilities->CallLogPop(1027);
6784  return(false);
6785  }
6786  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6787  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6788  {
6789  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6790  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6791  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6792  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6793  {
6794  Utilities->CallLogPop(1028);
6795  return(true);
6796  }
6797  }
6798  Utilities->CallLogPop(1029);
6799  return(false);
6800 }
6801 
6802 // ---------------------------------------------------------------------------
6803 
6804 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6805 { //same RepeatNumber (i.e. 0) used for the new shuttle
6806 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6807  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6808  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6809  if(PowerAtRail < 1)
6810  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6811  {
6813  {
6814  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6815  }
6817  Utilities->CallLogPop(2143);
6818  return;
6819  }
6820  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6821 
6822  if(!NoLogFlag)
6823  {
6825  }
6826  UnplotTrain(4);
6829  StartSpeed = 0;
6834  HeadCode = NewHeadCode;
6835  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6836  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6837  StoppedAtLocation = true;
6838  PlotStartPosition(6);
6840  // pale green
6843  TerminatedMessageSent = false;
6844  Utilities->CallLogPop(1078);
6845 }
6846 
6847 // ---------------------------------------------------------------------------
6848 
6849 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6850 // need to check whether all repeats finished or not
6851 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6852  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6853  if(RemainHereLogNotSent) // to prevent repeated logs
6854  {
6855  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6856  RemainHereLogNotSent = false;
6857  }
6859  // finished all repeats
6860  {
6862  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6865  TerminatedMessageSent = true;
6866  // no need to clear message as no more actions
6867  }
6868  TimetableFinished = true;
6869  Utilities->CallLogPop(1080);
6870  return;
6871  }
6872  if(PowerAtRail < 1)
6873  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6874  {
6876  {
6877  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6878  }
6880  Utilities->CallLogPop(2144);
6881  return;
6882  }
6883  int TempRepeatNumber = RepeatNumber + 1;
6884  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6885  // until after LogAction or the wrong time will be used
6886  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6887 
6888  if(!NoLogFlag)
6889  {
6891  }
6892  RepeatNumber++;
6893  UnplotTrain(5);
6896  StartSpeed = 0;
6901  HeadCode = NewHeadCode;
6902  StoppedAtLocation = true;
6903  PlotStartPosition(7);
6905  // pale green
6908  TerminatedMessageSent = false;
6909  Utilities->CallLogPop(1079);
6910 }
6911 
6912 // ---------------------------------------------------------------------------
6913 
6914 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
6915 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6916  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6917  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6918  if(PowerAtRail < 1)
6919  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6920  {
6922  {
6923  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6924  }
6926  Utilities->CallLogPop(2145);
6927  return;
6928  }
6930  // finished all repeats
6931  {
6932  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6933  if(!NoLogFlag)
6934  {
6936  }
6937  RepeatNumber = 0;
6938  UnplotTrain(6);
6941  StartSpeed = 0;
6943  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6944  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6946  HeadCode = NewHeadCode;
6947  StoppedAtLocation = true;
6948  PlotStartPosition(9);
6952  TerminatedMessageSent = false;
6953  Utilities->CallLogPop(1081);
6954  return;
6955  }
6956  int TempRepeatNumber = RepeatNumber + 1;
6957  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6958  // until after LogAction or the wrong time will be used
6959  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6960 
6961  if(!NoLogFlag)
6962  {
6964  }
6965  RepeatNumber++;
6966  UnplotTrain(7);
6969  StartSpeed = 0;
6974  HeadCode = NewHeadCode;
6975  StoppedAtLocation = true;
6976  PlotStartPosition(8);
6978  // pale green
6981  TerminatedMessageSent = false;
6982  Utilities->CallLogPop(1082);
6983 }
6984 
6985 // ---------------------------------------------------------------------------
6986 
6988 {
6989  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6990  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6991  // must be preceded by a TimeLoc departure
6992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6993  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6994  {
6996  {
6997  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6998  {
6999  Utilities->CallLogPop(1083);
7000  return(false);
7001  }
7002  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
7003  {
7004  Utilities->CallLogPop(1084);
7005  return(true);
7006  }
7007  }
7008  }
7009  Utilities->CallLogPop(1085);
7010  return(false);
7011 }
7012 
7013 // ---------------------------------------------------------------------------
7014 
7015 bool TTrain::AbleToMove(int Caller)
7016 {
7017  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
7018  bool Able = true;
7019 
7020  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
7021  {
7022  // StoppedForTrainInFront removed as tested below
7023  Utilities->CallLogPop(2146); // added v2.4.0
7024  return(false); // added v2.4.0
7025  }
7026  if(LeadElement > -1)
7027  {
7028  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
7029  {
7030  StoppedForTrainInFront = false;
7031  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
7032  Utilities->CallLogPop(2456);
7033  return(false);
7034  }
7035  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
7036  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
7037  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront) //check if train in front still there
7038  {
7039  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
7040  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
7041  {
7042  Able = true;
7043  StoppedForTrainInFront = false;
7044  }
7045  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 == -1))
7046  {
7047  Able = true;
7048  StoppedForTrainInFront = false;
7049  }
7050  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 == -1))
7051  {
7052  Able = true;
7053  StoppedForTrainInFront = false;
7054  }
7055  else
7056  {
7057  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
7058  }
7059  }
7060  }
7061  else // leaving at a continuation so keep going
7062  {
7063  Able = true;
7064  StoppedForTrainInFront = false;
7065  }
7066  Utilities->CallLogPop(1454);
7067  return(Able);
7068 }
7069 
7070 // ---------------------------------------------------------------------------
7071 
7073 {
7074  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
7075  // won't be set; if there is a train then set StoppedForTrainInFront
7076  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
7077  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
7078  if(LeadElement == -1) // exiting at continuation
7079  {
7080  Utilities->CallLogPop(2045);
7081  return(false);
7082  }
7083  // end of addition
7084  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
7085  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
7086 
7087  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
7088  {
7089  StoppedForTrainInFront = true;
7090  Utilities->CallLogPop(1455);
7091  return(false);
7092  }
7093  else
7094  {
7095  Utilities->CallLogPop(1456);
7097  // StoppedWithoutPower added v2.4.0
7098  }
7099 }
7100 
7101 // ---------------------------------------------------------------------------
7102 
7104 {
7105  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
7106  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
7107  TColor TempColour = BackgroundColour;
7108 
7109  UnplotTrain(8);
7112  StartSpeed = 0;
7113  PlotStartPosition(2);
7114  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
7115 
7116  //now erase a stub route if there is one, added at v2.5.1
7117  //first element of route is now immediately behind the train (i.e. next to MidElement)
7118  if(MidEntryPos >= 0)
7119  {
7120  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
7121  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
7122  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
7123  int RouteNumber = -1;
7124  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
7125  if(RouteType == TAllRoutes::NotAutoSigsRoute)
7126  {
7127  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
7128  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
7129  //elements can continue to be removed from that route
7130  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
7131  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
7132  {
7133  bool FirstPass = true; //added at v2.8.0
7134  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
7135  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
7136  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
7137  int TVPos2 = PDE.GetTrackVectorPosition();
7138  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
7139  {
7140  break;
7141  }
7142  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
7144  {
7145  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
7146  }
7147  else
7148  {
7149  break;
7150  }
7151  FirstPass = false;
7152  }
7153  AllRoutes->RebuildRailwayFlag = true;
7154  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
7155  }
7156  }
7157  }
7158  Utilities->CallLogPop(1102);
7159 }
7160 
7161 // ---------------------------------------------------------------------------
7162 
7164 {
7165  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7166  ",FloatingLabelNextString" + "," + HeadCode);
7167  AnsiString RetStr = "", LocationName = "";
7168 
7169  if(int(DelayedRandMins) > 0)
7170  {
7171  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7172  {
7173  throw Exception("Error - start entry in FloatingLabelNextString");
7174  }
7175  if(Ptr->FormatType == TimeTimeLoc)
7176  {
7177  if(TrainMode == Timetable)
7178  {
7179  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7180  // not arrived yet in tt mode
7181  {
7182  RetStr = "Arrive " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7183  }
7184  else
7185  {
7186  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7187  }
7188  }
7189  else // TrainMode == Signaller
7190  {
7191  if(!DepartureTimeSet) // not arrived yet
7192  {
7193  RetStr = "Arrive " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7194  }
7195  else
7196  {
7197  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7198  }
7199  }
7200  }
7201  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7202  {
7203  RetStr = "Arrive " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7204  }
7205  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7206  {
7207  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7208  }
7209  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7210  {
7211  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7212  }
7213  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7214  {
7215  RetStr = "Pass " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7216  }
7217  else if(Ptr->Command == "Fns")
7218  {
7219  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7220  Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7221  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7222  }
7223  else if(Ptr->Command == "F-nshs")
7224  {
7225  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " approx. " +
7226  Utilities->Format96HHMM(GetTrainTime(32, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7227  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7228  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7229  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7230  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7231  }
7232  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7233  {
7234  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7235  Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7236  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7237  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7238  }
7239  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7240  {
7241  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7242  +" at " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7243  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7244  }
7245  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7246  {
7247  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7248  Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7249  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7250  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7251  }
7252  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7253  {
7254  RetStr ="None, train terminated at " + Ptr->LocationName;
7255  }
7256  else if(Ptr->Command == "Frh")
7257  {
7258  RetStr = "None, train terminated at " + Ptr->LocationName;
7259  }
7260  else if(Ptr->Command == "Fer")
7261  {
7262  AnsiString AllowedExits = "";
7263  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " approx. " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime + TDateTime(DelayedRandMins/1440))) + AllowedExits;
7264  }
7265  else if(Ptr->Command == "Fjo")
7266  {
7267  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " approx. " +
7268  Utilities->Format96HHMM(GetTrainTime(11, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7269  }
7270  else if(Ptr->Command == "jbo")
7271  {
7272  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7273  " approx. " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7274  }
7275  else if(Ptr->Command == "fsp")
7276  {
7277  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7278  " approx. " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7279  }
7280  else if(Ptr->Command == "rsp")
7281  {
7282  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7283  " approx. " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7284  }
7285  else if(Ptr->Command == "cdt")
7286  {
7287  RetStr = "Change direction at " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7288  }
7289  }
7290  else
7291  {
7292  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7293  {
7294  throw Exception("Error - start entry in FloatingLabelNextString");
7295  }
7296  if(Ptr->FormatType == TimeTimeLoc)
7297  {
7298  if(TrainMode == Timetable)
7299  {
7300  if(!TrainAtLocation(3, LocationName) || (LocationName != Ptr->LocationName))
7301  // not arrived yet in tt mode
7302  {
7303  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(48, Ptr->ArrivalTime));
7304  }
7305  else
7306  {
7307  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7308  }
7309  }
7310  else // TrainMode == Signaller
7311  {
7312  if(!DepartureTimeSet) // not arrived yet
7313  {
7314  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(49, Ptr->ArrivalTime));
7315  }
7316  else
7317  {
7318  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7319  }
7320  }
7321  }
7322  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7323  {
7324  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(50, Ptr->ArrivalTime));
7325  }
7326  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7327  {
7328  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7329  }
7330  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7331  {
7332  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(51, Ptr->EventTime));
7333  }
7334  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7335  {
7336  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(52, Ptr->EventTime));
7337  }
7338  else if(Ptr->Command == "Fns")
7339  {
7340  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(53, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7341  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(53, Ptr->EventTime));
7342  RetStr = GetNewServiceDepartureInfo(10, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7343  }
7344  else if(Ptr->Command == "F-nshs")
7345  {
7346  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7348  RetStr = GetNewServiceDepartureInfo(12, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7349  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7350  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7351  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7352  }
7353  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7354  {
7355  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(54, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7356  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(55, Ptr->EventTime));
7357  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7358  RetStr = GetNewServiceDepartureInfo(14, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7359  }
7360  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7361  {
7362  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7363  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(56, Ptr->EventTime));
7364  RetStr = GetNewServiceDepartureInfo(16, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7365  }
7366  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7367  {
7368  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(55, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7369  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(57, Ptr->EventTime));
7370  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7371  RetStr = GetNewServiceDepartureInfo(18, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7372  }
7373  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7374  {
7375  RetStr ="None, train terminated at " + Ptr->LocationName;
7376  }
7377  else if(Ptr->Command == "Frh")
7378  {
7379  RetStr = "None, train terminated at " + Ptr->LocationName;
7380  }
7381  else if(Ptr->Command == "Fer")
7382  {
7383  AnsiString AllowedExits = "";
7384  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(4, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(62, Ptr->EventTime)) + AllowedExits;
7385  }
7386  else if(Ptr->Command == "Fjo")
7387  {
7388  RetStr = "Join " + TrainController->GetRepeatHeadCode(56, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7390  }
7391  else if(Ptr->Command == "jbo")
7392  {
7393  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(57, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7394  " at " + Utilities->Format96HHMM(GetTrainTime(59, Ptr->EventTime));
7395  }
7396  else if(Ptr->Command == "fsp")
7397  {
7398  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(58, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7399  " at " + Utilities->Format96HHMM(GetTrainTime(60, Ptr->EventTime));
7400  }
7401  else if(Ptr->Command == "rsp")
7402  {
7403  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(59, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7404  " at " + Utilities->Format96HHMM(GetTrainTime(61, Ptr->EventTime));
7405  }
7406  else if(Ptr->Command == "cdt")
7407  {
7408  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(63, Ptr->EventTime));
7409  }
7410  }
7411  Utilities->CallLogPop(1124);
7412  return(RetStr);
7413 }
7414 
7415 // ---------------------------------------------------------------------------
7416 /* as was
7417 AnsiString TTrain::FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
7418 {
7419  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7420  ",FloatingLabelNextString" + "," + HeadCode);
7421  AnsiString RetStr = "", LocationName = "";
7422 
7423  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7424  {
7425  throw Exception("Error - start entry in FloatingLabelNextString");
7426  }
7427  if(Ptr->FormatType == TimeTimeLoc)
7428  {
7429  if(TrainMode == Timetable)
7430  {
7431  if(!TrainAtLocation(, LocationName) || (LocationName != Ptr->LocationName))
7432  // not arrived yet in tt mode
7433  {
7434  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7435  }
7436  else
7437  {
7438  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7439  }
7440  }
7441  else // TrainMode == Signaller
7442  {
7443  if(!DepartureTimeSet) // not arrived yet
7444  {
7445  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7446  }
7447  else
7448  {
7449  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7450  }
7451  }
7452  }
7453  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7454  {
7455  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7456  }
7457  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7458  {
7459  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7460  }
7461  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7462  {
7463  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7464  }
7465  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7466  {
7467  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7468  }
7469  else if(Ptr->Command == "Fns")
7470  {
7471  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7472  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7473  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7474  }
7475  else if(Ptr->Command == "F-nshs")
7476  {
7477  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7478  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7479  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7480  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7481  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7482  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7483  }
7484  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7485  {
7486  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7487  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7488  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7489  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7490  }
7491  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7492  {
7493  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7494  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7495  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7496  }
7497  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7498  {
7499  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7500  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7501  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7502  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7503  }
7504  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7505  {
7506  RetStr ="None, train terminated at " + Ptr->LocationName;
7507  }
7508  else if(Ptr->Command == "Frh")
7509  {
7510  RetStr = "None, train terminated at " + Ptr->LocationName;
7511  }
7512  else if(Ptr->Command == "Fer")
7513  {
7514  AnsiString AllowedExits = "";
7515  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime)) + AllowedExits;
7516  }
7517  else if(Ptr->Command == "Fjo")
7518  {
7519  RetStr = "Join " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7520  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7521  }
7522  else if(Ptr->Command == "jbo")
7523  {
7524  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7525  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7526  }
7527  else if(Ptr->Command == "fsp")
7528  {
7529  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7530  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7531  }
7532  else if(Ptr->Command == "rsp")
7533  {
7534  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7535  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7536  }
7537  else if(Ptr->Command == "cdt")
7538  {
7539  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7540  }
7541  Utilities->CallLogPop();
7542  return(RetStr);
7543 }
7544 */
7545 // ---------------------------------------------------------------------------
7546 
7547 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
7548 {
7549  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7550  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7551  AnsiString DepTime = "", EventTime = "";
7552  bool CDTFlag = false; //reports if train changes direction before departs
7553  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7554  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7555  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7556  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7557  {
7558  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7559  {
7560  TowardsLocation = AVI->LocationName;
7561  }
7562  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7563  {
7564  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7565  if(TE.ActiveTrackElementName != "")
7566  {
7567  TowardsLocation = TE.ActiveTrackElementName;
7568  }
7569  else
7570  {
7571  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7572  }
7573  }
7574  }
7575  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7576  {
7577  if(AVI->Command == "cdt")
7578  {
7579  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7580  continue;
7581  }
7582  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7583  {
7584  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
7585  RetStr += "\nNew service splits at " + EventTime;
7586  Utilities->CallLogPop(2234);
7587  return(RetStr);
7588  }
7589  if(AVI->Command == "jbo")
7590  {
7591  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
7592  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
7593  Utilities->CallLogPop(2235);
7594  return(RetStr);
7595  }
7596  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7597  {
7598  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7599  if(CDTFlag)
7600  {
7601  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7602  {
7603  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7604  }
7605  else
7606  {
7607  RetStr += "\nNew service changes direction then departs at " + DepTime;
7608  }
7609  }
7610  else
7611  {
7612  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7613  {
7614  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7615  }
7616  else
7617  {
7618  RetStr += "\nNew service departs at " + DepTime;
7619  }
7620  }
7621  Utilities->CallLogPop(2236);
7622  return(RetStr);
7623  }
7624  }
7625  Utilities->CallLogPop(2208);
7626  return(RetStr); //if reach here then RetStr doesn't change
7627 }
7628 
7629 // ---------------------------------------------------------------------------
7630 
7632 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7633 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7634 {
7635  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7636  ",FloatingTimetableString" + "," + HeadCode);
7637  AnsiString RetStr = "", PartStr = "";
7638  int Count = 0;
7639  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7640  AnsiString LocName = Ptr->LocationName;
7641 
7642  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7643  // can start in signaller control so exclude this
7644  {
7645  throw Exception("Error - start entry in FloatingTimetableString");
7646  }
7647  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7648  bool FirstPass = true;
7649  Ptr--; // because incremented at start of loop
7650 
7651  // different first TimeTimeLoc display if in signaller control
7652  do
7653  {
7654  Ptr++;
7655  if((Ptr->FormatType == Repeat) || TimetableFinished)
7656  {
7657  break;
7658  }
7659  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7660  {
7661  AnsiString TrainLoc = "";
7662  if(TrainMode == Timetable)
7663  {
7664  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7665  {
7666  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7667  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7668  {
7669  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7670  }
7671  }
7672  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7673  {
7674  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7675  }
7676  else
7677  {
7678  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7679  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7680  Count++; // because there are 2 entries
7681  }
7682  }
7683  else // TrainMode == Signaller
7684  {
7685  if(DepartureTimeSet)
7686  {
7687  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7688  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7689  {
7690  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7691  }
7692  }
7693  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7694  {
7695  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7696  }
7697  else
7698  {
7699  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7700  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7701  Count++; // because there are 2 entries
7702  }
7703  }
7704  }
7705  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7706  {
7707  AnsiString TrainLoc = "";
7708  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7709  {
7710  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7711  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7712  {
7713  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7714  }
7715  }
7716  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7717  {
7718  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7719  }
7720  else
7721  {
7722  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7723  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7724  Count++; // because there are 2 entries
7725  }
7726  }
7727  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7728  {
7729  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7730  }
7731  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7732  {
7733  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7734  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7735  {
7736  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7737  }
7738  }
7739  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
7740  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
7741  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
7742  }
7743  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7744  {
7745  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7746  }
7747  else if(Ptr->Command == "Fns")
7748  {
7749  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7750  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7751  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7752  }
7753  else if(Ptr->Command == "F-nshs")
7754  {
7755  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7756  Ptr->LocationName;
7757  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7758  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7759  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7760  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7761  }
7762  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7763  {
7764  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7765  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7766  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7767  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7768  }
7769  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7770  {
7771  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7772  +" at " + Ptr->LocationName;
7773  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7774  }
7775  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7776  {
7777  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7778  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7779  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7780  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7781  }
7782  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7783  {
7784  PartStr = "Terminate at " + Ptr->LocationName;
7785  }
7786  else if(Ptr->Command == "Frh")
7787  {
7788  PartStr = "Terminate at " + Ptr->LocationName;
7789  }
7790  else if(Ptr->Command == "Fer")
7791  {
7792  AnsiString AllowedExits = "";
7793  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7794  }
7795  else if(Ptr->Command == "Fjo")
7796  {
7797  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7798  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7799  }
7800  else if(Ptr->Command == "jbo")
7801  {
7802  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7803  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7804  }
7805  else if(Ptr->Command == "fsp")
7806  {
7807  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7808  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7809  }
7810  else if(Ptr->Command == "rsp")
7811  {
7812  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7813  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7814  }
7815  else if(Ptr->Command == "cdt")
7816  {
7817  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7818  }
7819  if(RetStr != "")
7820  {
7821  RetStr = RetStr + '\n' + PartStr;
7822  }
7823  else
7824  {
7825  RetStr = PartStr;
7826  }
7827  FirstPass = false;
7828  Count++;
7829 
7830  if(SkipDep)
7831  {
7832  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
7833  Ptr--; //it is incremented at the start of the next loop
7834  SkipDep = false;
7835  SkipDepActedOn = true;
7836  }
7837  }
7838  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7839  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7840  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7841  // forward as anyone should wish to see without looking at the full timetable
7842  if(TimetableFinished)
7843  {
7844  if(TrainMode == Timetable)
7845  {
7846  RetStr = "Timetable finished";
7847  }
7848  else
7849  {
7850  RetStr = "No timetable";
7851  }
7852  }
7853  Utilities->CallLogPop(1125);
7854  return("Timetable:\n" + RetStr);
7855 }
7856 
7857 // ---------------------------------------------------------------------------
7858 
7859 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7860 {
7861  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7862  Utilities->SaveFileString(OutFile, HeadCode);
7865  Utilities->SaveFileInt(OutFile, StartSpeed);
7868  Utilities->SaveFileInt(OutFile, RepeatNumber);
7871  Utilities->SaveFileInt(OutFile, Mass);
7874  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7881  Utilities->SaveFileDouble(OutFile, BrakeRate);
7885  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7886  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7887  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7888  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7889  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7890  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7894  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7899  Utilities->SaveFileBool(OutFile, Derailed);
7901  Utilities->SaveFileBool(OutFile, Crashed);
7908  Utilities->SaveFileBool(OutFile, NotInService);
7909  Utilities->SaveFileBool(OutFile, Plotted);
7910  Utilities->SaveFileBool(OutFile, TrainGone);
7911  Utilities->SaveFileBool(OutFile, SPADFlag);
7913  Utilities->SaveFileInt(OutFile, HOffset[0]);
7914  Utilities->SaveFileInt(OutFile, HOffset[1]);
7915  Utilities->SaveFileInt(OutFile, HOffset[2]);
7916  Utilities->SaveFileInt(OutFile, HOffset[3]);
7917  Utilities->SaveFileInt(OutFile, VOffset[0]);
7918  Utilities->SaveFileInt(OutFile, VOffset[1]);
7919  Utilities->SaveFileInt(OutFile, VOffset[2]);
7920  Utilities->SaveFileInt(OutFile, VOffset[3]);
7921  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7922  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7923  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7924  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7925  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7926  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7927  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7928  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7930  Utilities->SaveFileInt(OutFile, (short)Straddle);
7931  Utilities->SaveFileInt(OutFile, NextTrainID);
7932  Utilities->SaveFileInt(OutFile, TrainID);
7933  Utilities->SaveFileInt(OutFile, LeadElement);
7934  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7935  Utilities->SaveFileInt(OutFile, LeadExitPos);
7936  Utilities->SaveFileInt(OutFile, MidElement);
7937  Utilities->SaveFileInt(OutFile, MidEntryPos);
7938  Utilities->SaveFileInt(OutFile, MidExitPos);
7939  Utilities->SaveFileInt(OutFile, LagElement);
7940  Utilities->SaveFileInt(OutFile, LagEntryPos);
7941  Utilities->SaveFileInt(OutFile, LagExitPos);
7942  int ColourNumber;
7943 
7945  {
7946  ColourNumber = 0;
7947  }
7949  {
7950  ColourNumber = 1;
7951  }
7953  {
7954  ColourNumber = 2;
7955  }
7957  {
7958  ColourNumber = 3;
7959  }
7961  {
7962  ColourNumber = 4;
7963  }
7965  {
7966  ColourNumber = 5;
7967  }
7969  {
7970  ColourNumber = 6;
7971  }
7973  {
7974  ColourNumber = 7;
7975  }
7977  {
7978  ColourNumber = 8;
7979  }
7981  {
7982  ColourNumber = 9;
7983  }
7985  {
7986  ColourNumber = 10;
7987  }
7989  {
7990  ColourNumber = 11;
7991  }
7993  {
7994  ColourNumber = 12;
7995  }
7996  else if(BackgroundColour == clTRSBackground)
7997  {
7998  ColourNumber = 13;
7999  }
8001  {
8002  ColourNumber = 14; // added at v2.4.0
8003  }
8004  Utilities->SaveFileInt(OutFile, ColourNumber);
8005 
8006  // additional data
8007  bool ForwardHeadCode;
8008 
8009  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
8010  {
8011  ForwardHeadCode = true;
8012  }
8013  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
8014  else
8015  {
8016  ForwardHeadCode = false;
8017  }
8018  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
8019 
8020  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
8021 
8022  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
8023  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
8024 
8025  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
8026  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
8027  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
8028  // so use the last asterisk position for this - 0 for false & 1 for true
8029  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
8030  AnsiString Marker;
8031 
8033  {
8034  Marker = "*****1";
8035  }
8036  else
8037  {
8038  Marker = "*****0";
8039  }
8040  if(RestoreTimetableLocation == "")
8041  {
8042  Utilities->SaveFileString(OutFile, Marker);
8043  }
8044  else
8045  {
8046  AnsiString CombinedString = RestoreTimetableLocation + Marker;
8047  Utilities->SaveFileString(OutFile, CombinedString);
8048  // RestoreTimetableLocation + marker
8049  }
8050  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
8051  Utilities->CallLogPop(1457);
8052 }
8053 
8054 // ---------------------------------------------------------------------------
8055 
8056 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
8057 {
8058  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
8059  HeadCode = Utilities->LoadFileString(InFile);
8062  StartSpeed = Utilities->LoadFileInt(InFile);
8064  if(SignallerMaxSpeed < 10)
8065  {
8066  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
8067  }
8069  RepeatNumber = Utilities->LoadFileInt(InFile);
8072  Mass = Utilities->LoadFileInt(InFile);
8075  {
8077  }
8078  // above added at v2.1.0 for legacy session files where value may not have been limited
8080  EntrySpeed = Utilities->LoadFileDouble(InFile);
8084  if(TimetableMaxRunningSpeed < 10)
8085  {
8086  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8087  }
8089  if(MaxRunningSpeed < 10)
8090  {
8091  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8092  }
8095  BrakeRate = Utilities->LoadFileDouble(InFile);
8099  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
8100  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
8101  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
8102  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
8103  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
8104  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
8113  Derailed = Utilities->LoadFileBool(InFile);
8115  Crashed = Utilities->LoadFileBool(InFile);
8122  NotInService = Utilities->LoadFileBool(InFile);
8123  Plotted = Utilities->LoadFileBool(InFile);
8124  TrainGone = Utilities->LoadFileBool(InFile);
8125  SPADFlag = Utilities->LoadFileBool(InFile);
8127  HOffset[0] = Utilities->LoadFileInt(InFile);
8128  HOffset[1] = Utilities->LoadFileInt(InFile);
8129  HOffset[2] = Utilities->LoadFileInt(InFile);
8130  HOffset[3] = Utilities->LoadFileInt(InFile);
8131  VOffset[0] = Utilities->LoadFileInt(InFile);
8132  VOffset[1] = Utilities->LoadFileInt(InFile);
8133  VOffset[2] = Utilities->LoadFileInt(InFile);
8134  VOffset[3] = Utilities->LoadFileInt(InFile);
8135  PlotElement[0] = Utilities->LoadFileInt(InFile);
8136  PlotElement[1] = Utilities->LoadFileInt(InFile);
8137  PlotElement[2] = Utilities->LoadFileInt(InFile);
8138  PlotElement[3] = Utilities->LoadFileInt(InFile);
8139  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
8140  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
8141  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
8142  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
8144  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
8145  NextTrainID = Utilities->LoadFileInt(InFile);
8146  // will be same for all but best to save all anyway
8147  TrainID = Utilities->LoadFileInt(InFile);
8148  LeadElement = Utilities->LoadFileInt(InFile);
8149  LeadEntryPos = Utilities->LoadFileInt(InFile);
8150  LeadExitPos = Utilities->LoadFileInt(InFile);
8151  MidElement = Utilities->LoadFileInt(InFile);
8152  MidEntryPos = Utilities->LoadFileInt(InFile);
8153  MidExitPos = Utilities->LoadFileInt(InFile);
8154  LagElement = Utilities->LoadFileInt(InFile);
8155  LagEntryPos = Utilities->LoadFileInt(InFile);
8156  LagExitPos = Utilities->LoadFileInt(InFile);
8157  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
8158 
8159  if(ColourNumber == 0)
8160  {
8162  }
8163  else if(ColourNumber == 1)
8164  {
8166  }
8167  else if(ColourNumber == 2)
8168  {
8170  }
8171  else if(ColourNumber == 3)
8172  {
8174  }
8175  else if(ColourNumber == 4)
8176  {
8178  }
8179  else if(ColourNumber == 5)
8180  {
8182  }
8183  else if(ColourNumber == 6)
8184  {
8186  }
8187  else if(ColourNumber == 7)
8188  {
8190  }
8191  else if(ColourNumber == 8)
8192  {
8194  }
8195  else if(ColourNumber == 9)
8196  {
8198  }
8199  else if(ColourNumber == 10)
8200  {
8202  }
8203  else if(ColourNumber == 11)
8204  {
8206  }
8207  else if(ColourNumber == 12)
8208  {
8210  }
8211  else if(ColourNumber == 13)
8212  {
8214  }
8215  else if(ColourNumber == 14)
8216  {
8217  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
8218 
8219  }
8220  // additional data
8222  // sets the BackgroundColour to the loaded value
8223  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
8224 
8225  if(ForwardHeadCode)
8226  {
8227  for(int x = 0; x < 4; x++)
8228  {
8230  }
8231  }
8232  else
8233  {
8234  for(int x = 0; x < 4; x++)
8235  {
8236  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
8237  }
8238  }
8239  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
8240  if(TrainMode == Timetable)
8241  {
8242  if(Crashed)
8243  {
8245  }
8246  else
8247  {
8249  }
8250  }
8251  else
8252  {
8254  }
8256  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
8257  if(Straddle == LeadMid)
8258  {
8259  if(LeadElement > -1)
8260  {
8262  }
8263  if(LeadElement > -1)
8264  {
8266  }
8267  if(MidElement > -1)
8268  {
8270  }
8271  if(MidElement > -1)
8272  {
8274  }
8275  }
8276  else if(Straddle == LeadMidLag)
8277  {
8278  if(LeadElement > -1)
8279  {
8281  }
8282  if(MidElement > -1)
8283  {
8285  }
8286  if(MidElement > -1)
8287  {
8289  }
8290  if(LagElement > -1)
8291  {
8293  }
8294  }
8295  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
8296 
8297  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8298  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8299 
8300  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8301 
8302  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8303  if(LeadElement > -1)
8304  // need to include this in case train exiting & no lead element
8305  {
8307  {
8308  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8309  }
8310  }
8311  AValue = sqrt(2 * PowerAtRail / Mass);
8312 
8313  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8314 
8315  // possible RestoreTimetableLocation + Marker, where Marker is
8316  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8317  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8318  // added at beta v0.2e
8319  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8320  // name not allowed to include the '*' character
8321  {
8322  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8323  bool GiveMessagesFalse = false;
8324  bool CheckLocationsExistInRailwayTrue = true;
8325  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8326  {
8327  // otherwise take no action
8328  RestoreTimetableLocation = Location;
8329  }
8330  }
8331  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8332 
8333  StoppedWithoutPower = false;
8334  if(Marker[6] == '1')
8335  {
8336  StoppedWithoutPower = true;
8337  }
8338  Utilities->CallLogPop(1458);
8339 }
8340 
8341 // ---------------------------------------------------------------------------
8342 
8343 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8344 {
8346  {
8347  return(false); // HeadCode
8348 
8349  }
8350  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8351  {
8352  return(false); // RearStartElement
8353 
8354  }
8355  if(!Utilities->CheckFileInt(InFile, 0, 3))
8356  {
8357  return(false); // RearStartExitPos
8358 
8359  }
8360  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8361  {
8362  return(false); // StartSpeed
8363 
8364  }
8365  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8366  {
8367  return(false); // SignallerMaxSpeed
8368 
8369  }
8370  if(!Utilities->CheckFileBool(InFile))
8371  {
8372  return(false); // HoldAtLocationInTTMode
8373 
8374  }
8375  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8376  {
8377  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8378 
8379  }
8380  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8381  {
8382  return(false); // IncrementalMinutes (max 96 x 60)
8383 
8384  }
8385  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8386  {
8387  return(false); // IncrementalDigits
8388 
8389  }
8390  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8391  {
8392  return(false); // Mass
8393 
8394  }
8395  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8396  {
8397  return(false);
8398  }
8399  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8400  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8401  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8402  {
8403  return(false); // FrontElementLength
8404 
8405  }
8406  if(!Utilities->CheckFileDouble(InFile))
8407  {
8408  return(false); // EntrySpeed
8409 
8410  }
8411  if(!Utilities->CheckFileDouble(InFile))
8412  {
8413  return(false); // ExitSpeedHalf
8414 
8415  }
8416  if(!Utilities->CheckFileDouble(InFile))
8417  {
8418  return(false); // ExitSpeedFull
8419 
8420  }
8421  if(!Utilities->CheckFileDouble(InFile))
8422  {
8423  return(false); // TimetableMaxRunningSpeed
8424 
8425  }
8426  if(!Utilities->CheckFileDouble(InFile))
8427  {
8428  return(false); // MaxRunningSpeed
8429 
8430  }
8431  if(!Utilities->CheckFileDouble(InFile))
8432  {
8433  return(false); // MaxExitSpeed
8434 
8435  }
8436  if(!Utilities->CheckFileDouble(InFile))
8437  {
8438  return(false); // MaxBrakeRate
8439 
8440  }
8441  if(!Utilities->CheckFileDouble(InFile))
8442  {
8443  return(false); // BrakeRate
8444 
8445  }
8446  if(!Utilities->CheckFileDouble(InFile))
8447  {
8448  return(false); // PowerAtRail
8449 
8450  }
8451  if(!Utilities->CheckFileBool(InFile))
8452  {
8453  return(false); // FirstHalfMove
8454 
8455  }
8456  if(!Utilities->CheckFileBool(InFile))
8457  {
8458  return(false); // OneLengthAccelDecel
8459 
8460  }
8461  if(!Utilities->CheckFileDouble(InFile))
8462  {
8463  return(false); // double(EntryTime)
8464 
8465  }
8466  if(!Utilities->CheckFileDouble(InFile))
8467  {
8468  return(false); // double(ExitTimeHalf)
8469 
8470  }
8471  if(!Utilities->CheckFileDouble(InFile))
8472  {
8473  return(false); // double(ExitTimeFull)
8474 
8475  }
8476  if(!Utilities->CheckFileDouble(InFile))
8477  {
8478  return(false); // double(ReleaseTime)
8479 
8480  }
8481  if(!Utilities->CheckFileDouble(InFile))
8482  {
8483  return(false); // double(TRSTime)
8484 
8485  }
8486  if(!Utilities->CheckFileDouble(InFile))
8487  {
8488  return(false); // double(LastActionTime)
8489 
8490  }
8491  if(!Utilities->CheckFileBool(InFile))
8492  {
8493  return(false); // CallingOnFlag
8494 
8495  }
8496  if(!Utilities->CheckFileBool(InFile))
8497  {
8498  return(false); // BeingCalledOn
8499 
8500  }
8501  if(!Utilities->CheckFileBool(InFile))
8502  {
8503  return(false); // DepartureTimeSet
8504 
8505  }
8506  if(!Utilities->CheckFileInt(InFile, 0, 2))
8507  {
8508  return(false); // (short)TrainMode
8509 
8510  }
8511  if(!Utilities->CheckFileBool(InFile))
8512  {
8513  return(false); // TimetableFinished
8514 
8515  }
8516  if(!Utilities->CheckFileBool(InFile))
8517  {
8518  return(false); // LastActionDelayFlag
8519 
8520  }
8521  if(!Utilities->CheckFileBool(InFile))
8522  {
8523  return(false); // SignallerRemoved
8524 
8525  }
8526  if(!Utilities->CheckFileBool(InFile))
8527  {
8528  return(false); // TerminatedMessageSent
8529 
8530  }
8531  if(!Utilities->CheckFileBool(InFile))
8532  {
8533  return(false); // Derailed
8534 
8535  }
8536  if(!Utilities->CheckFileBool(InFile))
8537  {
8538  return(false); // DerailPending
8539 
8540  }
8541  if(!Utilities->CheckFileBool(InFile))
8542  {
8543  return(false); // Crashed
8544 
8545  }
8546  if(!Utilities->CheckFileBool(InFile))
8547  {
8548  return(false); // StoppedAtBuffers
8549 
8550  }
8551  if(!Utilities->CheckFileBool(InFile))
8552  {
8553  return(false); // StoppedAtSignal
8554 
8555  }
8556  if(!Utilities->CheckFileBool(InFile))
8557  {
8558  return(false); // StoppedAtLocation
8559 
8560  }
8561  if(!Utilities->CheckFileBool(InFile))
8562  {
8563  return(false); // SignallerStopped
8564 
8565  }
8566  if(!Utilities->CheckFileBool(InFile))
8567  {
8568  return(false); // StoppedAfterSPAD
8569 
8570  }
8571  if(!Utilities->CheckFileBool(InFile))
8572  {
8573  return(false); // StoppedForTrainInFront
8574 
8575  }
8576  if(!Utilities->CheckFileBool(InFile))
8577  {
8578  return(false); // NotInService
8579 
8580  }
8581  if(!Utilities->CheckFileBool(InFile))
8582  {
8583  return(false); // Plotted
8584 
8585  }
8586  if(!Utilities->CheckFileBool(InFile))
8587  {
8588  return(false); // TrainGone
8589 
8590  }
8591  if(!Utilities->CheckFileBool(InFile))
8592  {
8593  return(false); // SPADFlag
8594 
8595  }
8596  if(!Utilities->CheckFileBool(InFile))
8597  {
8598  return(false); // TimeTimeLocArrived
8599 
8600  }
8601  if(!Utilities->CheckFileInt(InFile, 0, 15))
8602  {
8603  return(false); // HOffset[0]
8604 
8605  }
8606  if(!Utilities->CheckFileInt(InFile, 0, 15))
8607  {
8608  return(false); // HOffset[1]
8609 
8610  }
8611  if(!Utilities->CheckFileInt(InFile, 0, 15))
8612  {
8613  return(false); // HOffset[2]
8614 
8615  }
8616  if(!Utilities->CheckFileInt(InFile, 0, 15))
8617  {
8618  return(false); // HOffset[3]
8619 
8620  }
8621  if(!Utilities->CheckFileInt(InFile, 0, 15))
8622  {
8623  return(false); // VOffset[0]
8624 
8625  }
8626  if(!Utilities->CheckFileInt(InFile, 0, 15))
8627  {
8628  return(false); // VOffset[1]
8629 
8630  }
8631  if(!Utilities->CheckFileInt(InFile, 0, 15))
8632  {
8633  return(false); // VOffset[2]
8634 
8635  }
8636  if(!Utilities->CheckFileInt(InFile, 0, 15))
8637  {
8638  return(false); // VOffset[3]
8639 
8640  }
8641  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8642  {
8643  return(false); // PlotElement[0]
8644 
8645  }
8646  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8647  {
8648  return(false); // PlotElement[1]
8649 
8650  }
8651  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8652  {
8653  return(false); // PlotElement[2]
8654 
8655  }
8656  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8657  {
8658  return(false); // PlotElement[3]
8659 
8660  }
8661  if(!Utilities->CheckFileInt(InFile, 0, 3))
8662  {
8663  return(false); // PlotEntryPos[0]
8664 
8665  }
8666  if(!Utilities->CheckFileInt(InFile, 0, 3))
8667  {
8668  return(false); // PlotEntryPos[1]
8669 
8670  }
8671  if(!Utilities->CheckFileInt(InFile, 0, 3))
8672  {
8673  return(false); // PlotEntryPos[2]
8674 
8675  }
8676  if(!Utilities->CheckFileInt(InFile, 0, 3))
8677  {
8678  return(false); // PlotEntryPos[3]
8679 
8680  }
8681  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8682  {
8683  return(false); // TrainCrashedInto
8684 
8685  }
8686  if(!Utilities->CheckFileInt(InFile, 0, 2))
8687  {
8688  return(false); // (short)Straddle
8689 
8690  }
8691  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8692  {
8693  return(false); // NextTrainID
8694 
8695  }
8696  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8697  {
8698  return(false); // TrainID
8699 
8700  }
8701  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8702  {
8703  return(false); // LeadElement
8704 
8705  }
8706  if(!Utilities->CheckFileInt(InFile, 0, 3))
8707  {
8708  return(false); // LeadEntryPos
8709 
8710  }
8711  if(!Utilities->CheckFileInt(InFile, 0, 3))
8712  {
8713  return(false); // LeadExitPos
8714 
8715  }
8716  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8717  {
8718  return(false); // MidElement
8719 
8720  }
8721  if(!Utilities->CheckFileInt(InFile, 0, 3))
8722  {
8723  return(false); // MidEntryPos
8724 
8725  }
8726  if(!Utilities->CheckFileInt(InFile, 0, 3))
8727  {
8728  return(false); // MidExitPos
8729 
8730  }
8731  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8732  {
8733  return(false); // LagElement
8734 
8735  }
8736  if(!Utilities->CheckFileInt(InFile, 0, 3))
8737  {
8738  return(false); // LagEntryPos
8739 
8740  }
8741  if(!Utilities->CheckFileInt(InFile, 0, 3))
8742  {
8743  return(false); // LagExitPos
8744 
8745  }
8746  if(!Utilities->CheckFileInt(InFile, 0, 14))
8747  {
8748  return(false);
8749  }
8750  // Background colour number //14 is failed colour at v2.4.0
8751  if(!Utilities->CheckFileBool(InFile))
8752  {
8753  return(false); // ForwardHeadCode
8754 
8755  }
8756  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8757  {
8758  return(false); // TrainDataEntryValue
8759 
8760  }
8761  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8762  {
8763  return(false); // ActionVectorEntryValue
8764 
8765  }
8767  {
8768  return(false); // End of train marker + possible RestoreTimetableLocation
8769 
8770  }
8771  // and StoppedWithoutPower flag
8772  return(true);
8773 }
8774 
8775 // ---------------------------------------------------------------------------
8776 
8777 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8778 {
8779  // order below reflects significance so earlier shows first, as may have more than one flag set
8780  // only plot flashing trains when Flash is true
8781 
8782 /*
8783  clCrashedBackground (TColor)0x0000FF red
8784  clDerailedBackground (TColor)0x0000FF red
8785  clSPADBackground (TColor)0x00FFFF yellow
8786  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8787  clCallOnBackground (TColor)0xFF33FF light magenta
8788  clSignalStopBackground (TColor)0x00FF66 green
8789  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8790  clStationStopBackground (TColor)0xCCFFCC pale green
8791  clTRSBackground (TColor)0xFFCCFF light pink
8792  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8793  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8794  clSignallerStopped (TColor)0x99CCFF caramel
8795  clNormalBackground (TColor)0xCCCCCC grey
8796 */
8797 
8798  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8799  bool HideFlashingTrain = true;
8800  // hide it when Flash false so it blinks on and off
8801  // if don't hide it it stays displayed all the time
8802  Graphics::TBitmap *SmallTrainBitmap;
8803 
8804  // NB ensure retain same order as zoomed in order so colours correspond
8806  {
8807  TrainController->CrashWarning = true;
8808  SmallTrainBitmap = RailGraphics->smRed;
8809  }
8811  {
8813  SmallTrainBitmap = RailGraphics->smRed;
8814  }
8816  {
8817  TrainController->SPADWarning = true;
8818  SmallTrainBitmap = RailGraphics->smYellow;
8819  }
8821  {
8823  SmallTrainBitmap = RailGraphics->smOrange;
8824  }
8826  {
8828  SmallTrainBitmap = RailGraphics->smMagenta;
8829  }
8831  {
8833  SmallTrainBitmap = RailGraphics->smBrightGreen;
8834  }
8836  {
8838  SmallTrainBitmap = RailGraphics->smCyan;
8839  }
8841  {
8842  SmallTrainBitmap = RailGraphics->smPaleGreen;
8843  HideFlashingTrain = false;
8844  }
8846  {
8847  SmallTrainBitmap = RailGraphics->smCyan;
8848  HideFlashingTrain = false;
8849  }
8851  {
8852  SmallTrainBitmap = RailGraphics->smLightBlue;
8853  HideFlashingTrain = false;
8854  }
8856  {
8857  SmallTrainBitmap = RailGraphics->smCaramel;
8858  HideFlashingTrain = false;
8859  }
8860  else
8861  {
8862  SmallTrainBitmap = RailGraphics->smBlack; // moving
8863  HideFlashingTrain = false;
8864  }
8865  // now plot the new train
8866  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8867  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8868  {
8869  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8870  }
8871  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8872  {
8873  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8874  }
8875  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8876  {
8877  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8878  }
8882  Utilities->CallLogPop(1459);
8883 }
8884 
8885 // ---------------------------------------------------------------------------
8886 
8888 {
8889  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8890  if(!Display->ZoomOutFlag)
8891  {
8892  Utilities->CallLogPop(1304);
8893  return;
8894  }
8895  for(int y = 0; y < 3; y++)
8896  {
8897  if(OldZoomOutElement[y] > -1)
8898  {
8899  bool FoundFlag = false;
8900  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8901  TTrackElement IATElement1, IATElement2;
8902  // default elements to begin with
8903  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8904  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8905  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8906  if(FoundFlag)
8907  {
8908  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8909  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8910  if(IMPair.first != IMPair.second)
8911  {
8912  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8913  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8914  }
8915  }
8916  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8917  }
8918  }
8919  Utilities->CallLogPop(1305);
8920 }
8921 
8922 // ---------------------------------------------------------------------------
8923 
8924 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8925 {
8926  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8927  LocationName = "";
8928  if(!RevisedStoppedAtLoc())
8929  {
8930  Utilities->CallLogPop(1398);
8931  return(false);
8932  }
8933  if(LeadElement > -1)
8934  {
8936  }
8937  if((LocationName == "") && (MidElement > -1))
8938  {
8939  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8940  }
8941  if((LocationName == "") && (LagElement > -1))
8942  {
8943  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8944  }
8945  if(LocationName == "")
8946  {
8947  throw Exception("Error - Location name not set in TrainAtLocation");
8948  }
8949  Utilities->CallLogPop(1399);
8950  return(true);
8951 }
8952 
8953 // ---------------------------------------------------------------------------
8954 
8955 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8956 {
8957  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8958  for(int x = 0; x < 4; x++)
8959  {
8960  PlotTrainGraphic(7, x, Disp);
8961  }
8962  Utilities->CallLogPop(647);
8963 }
8964 
8965 // ---------------------------------------------------------------------------
8966 
8967 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8968 {
8969  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8970  for(int x = 0; x < 4; x++)
8971  {
8972  if(PlotElement[x] > -1)
8973  {
8974  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8975  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8976  }
8977  }
8978  Utilities->CallLogPop(1708);
8979 }
8980 
8981 // ---------------------------------------------------------------------------
8982 
8983 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8984 {
8985  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8986  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8987  AnsiString(LinkNumber) + "," + HeadCode);
8988 
8989 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8990  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8991  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8992 */
8993 
8994  // note that MidElement always fully occupied
8995  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8996  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8997  {
8998  Utilities->CallLogPop(2005);
8999  return(true);
9000  }
9001  if(Straddle == LeadMid)
9002  {
9003  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
9004  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
9005  {
9006  Utilities->CallLogPop(2006);
9007  return(true);
9008  }
9009  }
9010  else if(Straddle == LeadMidLag)
9011  {
9012  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
9013  // only interested in LeadEntryPos as train not occupying ExitPos yet
9014  {
9015  Utilities->CallLogPop(2007);
9016  return(true);
9017  }
9018  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
9019  // only interested in LagExitPos as train has left EntryPos
9020  {
9021  Utilities->CallLogPop(2008);
9022  return(true);
9023  }
9024  }
9025  Utilities->CallLogPop(2009);
9026  return(false);
9027 }
9028 
9029 // ---------------------------------------------------------------------------
9030 
9031 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
9036 // TimeToExit & ExitPair added for multiplayer
9037 {
9038  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
9039  int DistanceToRedSignal = 0, DistanceToExit = -1;
9040  float TimeToAct = 0, LastTimeToExit = TimeToExit;
9041  TimeToExit = -1;
9042  ExitPair.first = -1;
9043  ExitPair.second = -1;
9044  float MinsEarly = 0; //added at v2.6.1
9045  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
9046  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
9047  float TempTTE;
9048 
9049  if(TrainFailed)
9050  {
9051  Utilities->CallLogPop(2147);
9052  return(0); // time to act now, time to exit set above
9053  }
9054  if(SignallerStopped)
9055  {
9056  Utilities->CallLogPop(2080);
9057  return(-1); //time to exit set above
9058  }
9059  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
9060  {
9061  Utilities->CallLogPop(2266);
9062  return(-1); // time to exit set above
9063  }
9064 
9065  // check if if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
9066  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
9067  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9068  {
9069  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
9070  {
9071  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9072  {
9073  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9074  if(TempTTE < LastTimeToExit)
9075  {
9076  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9077  }
9078  else
9079  {
9080  TimeToExit = LastTimeToExit;
9081  }
9082  }
9083  else
9084  {
9085  TimeToExit = LastTimeToExit;
9086  }
9087  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
9088  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
9089  Utilities->CallLogPop(2342);
9090  return(-1);
9091  }
9092  else
9093  {
9094  TimeToExit = 0; //all train exited
9095  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
9096  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
9097  Utilities->CallLogPop(2343);
9098  return(-1);
9099  }
9100  }
9101  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
9102  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9103  {
9104  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
9105  {
9106  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9107  {
9108  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9109  if(TempTTE < LastTimeToExit)
9110  {
9111  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9112  }
9113  else
9114  {
9115  TimeToExit = LastTimeToExit;
9116  }
9117  }
9118  else
9119  {
9120  TimeToExit = LastTimeToExit;
9121  }
9122  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
9123  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
9124  Utilities->CallLogPop(2344);
9125  return(-1);
9126  }
9127  else //front element of train fully off the exit, one length to exit
9128  {
9129  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9130  {
9131  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9132  if(TempTTE < LastTimeToExit)
9133  {
9134  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9135  }
9136  else
9137  {
9138  TimeToExit = LastTimeToExit;
9139  }
9140  }
9141  else
9142  {
9143  TimeToExit = LastTimeToExit;
9144  }
9145  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
9146  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
9147  Utilities->CallLogPop(2345);
9148  return(-1);
9149  }
9150  }
9151  if(LeadElement > -1)
9152  {
9154  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
9155  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
9156  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9157  if(Straddle == LeadMidLag)
9158  {
9159  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9160  {
9161  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9162  if(TempTTE < LastTimeToExit)
9163  {
9164  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9165  }
9166  else
9167  {
9168  TimeToExit = LastTimeToExit;
9169  }
9170  }
9171  else
9172  {
9173  TimeToExit = LastTimeToExit;
9174  }
9175  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
9176  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
9177  Utilities->CallLogPop(2346);
9178  return(-1);
9179  }
9180  else
9181  {
9182  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9183  {
9184  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9185  if(TempTTE < LastTimeToExit)
9186  {
9187  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9188  }
9189  else
9190  {
9191  TimeToExit = LastTimeToExit;
9192  }
9193  }
9194  else
9195  {
9196  TimeToExit = LastTimeToExit;
9197  }
9198  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
9199  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
9200  Utilities->CallLogPop(2347);
9201  return(-1);
9202  }
9203  }
9204  }
9205 //here if LeadElement > -1 and its forward connection also > -1
9206 
9207  // calc distance to next red signal
9208  if(!Stopped() || RevisedStoppedAtLoc())
9209  {
9210  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
9211  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
9212  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
9213 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
9214  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
9215  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
9216  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
9217  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
9218  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
9219  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
9220  before the train has stopped the current station is still recognised as a future stop.
9221  In signaller mode stops don't count, and if pass stop signal command is given then when have LeadMidLag the current element
9222  becomes the signal, and the time to act indication becomes 'NOW'.
9223 */
9224  {
9225  FirstPosToBeMeasured = LeadElement;
9226  FirstEntryPos = LeadEntryPos;
9227  }
9228  float CurrentStopTime; // set to 0 at start of function
9229  float LaterStopTime; // set to 0 at start of function
9230  float RecoverableTime; // set to 0 at start of function
9231  int AvTrackSpeed; // set to zero at start of function
9232  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
9233  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
9234  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
9235 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
9236 //Therefore since need to calculate the time for each in the same way use a generic
9237 
9238  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
9239  {
9240  TimeToExit = -1;
9241  Utilities->CallLogPop(2076);
9242  return(-1);
9243  }
9244 //else one or other is set
9245  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
9246  bool DistanceToExitSet = (DistanceToExit > -1);
9247  int GenericDistance = DistanceToRedSignal;
9248  if(DistanceToExitSet)
9249  {
9250  GenericDistance = DistanceToExit;
9251  }
9252 /* Have MinsDelayed; pos or neg,
9253  CurrentStopTime; pos or zero
9254  LaterStopTime; pos or zero
9255  RecoverableTime; pos or zero
9256 
9257  & from these calculate TotalStopTime. noting that:
9258  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
9259  RecoverableTime always < LaterStopTime or both zero
9260  can't subtract more than RecoverableTime (MinsDelayed > 0)
9261  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
9262  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
9263  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
9264  if running early & stopped at location CurrentStopTime will automatically include the excess
9265 */
9266  float TimeToSubtract, TotalStopTime;
9267  if(MinsDelayed > RecoverableTime)
9268  {
9269  TimeToSubtract = RecoverableTime;
9270  }
9271  else
9272  {
9273  TimeToSubtract = MinsDelayed; // may be negative;
9274  }
9275  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
9276  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
9277  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
9278  //next station stop
9279  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
9280  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
9281  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
9282  //first find departure time from the next stop
9283  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
9284  {
9285  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
9286  {
9289  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9290  }
9291  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
9292  {
9294  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9295  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
9296  {
9297  // must be a departure
9298  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9299  }
9300  }
9301  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9302  {
9303  MinsEarly = 0;
9304  }
9305  if(MinsEarly < 0)
9306  {
9307  MinsEarly = 0;
9308  }
9309  }
9310  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9311  {
9312  if(CurrentStopTime > 0)
9313  {
9314  TotalStopTime = CurrentStopTime + LaterStopTime;
9315  }
9316  // stopped at loc, will depart on time
9317  else
9318  {
9319  TotalStopTime = LaterStopTime - MinsDelayed;
9320  }
9321  // not stopped, will depart on time at first later stop so add the delay
9322  }
9323  else if((MinsEarly > 0) && !Stopped()) //running early
9324  {
9325  TotalStopTime = LaterStopTime + MinsEarly;
9326  }
9327  else // on time or running late
9328  {
9329  if(LaterStopTime == 0)
9330  {
9331  TotalStopTime = CurrentStopTime;
9332  }
9333  // no later stops, if stopped now will depart as soon as possible,
9334  // if not stopped no stop times to add
9335  else
9336  {
9337  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9338  }
9339  }
9340  if(AvTrackSpeed < 30)
9341  {
9342  AvTrackSpeed = 30;
9343  }
9344  int Speed = AvTrackSpeed;
9345  if(AvTrackSpeed > int(MaxRunningSpeed))
9346  {
9347  Speed = int(MaxRunningSpeed);
9348  }
9349  if(TrainMode == Signaller)
9350  {
9351  Speed = SignallerMaxSpeed;
9352  TotalStopTime = 0;
9353  }
9354  if(DistanceToRedSignalSet)
9355  {
9356  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9357  // accel & decel taken into account in
9358  // CalcDistanceToRedSignalandStopTime
9359  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9360  TimeToExit = -1;
9361  Utilities->CallLogPop(2079);
9362  return(TimeToAct);
9363  }
9364  else //DistanceToExitSet must be true
9365  {
9366  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9367  // accel & decel taken into account in
9368  // CalcDistanceToRedSignalandStopTime
9369  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9370  Utilities->CallLogPop(2370);
9371  return(-1); //no red signal so no time to act
9372  }
9373  }
9374  else // stopped not at location
9375  {
9377  {
9378  TimeToAct = 0.0;
9379  TimeToExit = -1;
9380  }
9381  // but if stopped at a signal & autosigs route after it then ok
9382  if(StoppedAtSignal)
9383  {
9384  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9385  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9386  int NextExitPos;
9387  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9388  {
9389  if((NextEntryPos == 0) || (NextEntryPos == 2))
9390  // leading entry point
9391  {
9392  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9393  {
9394  NextExitPos = 1;
9395  }
9396  else
9397  {
9398  NextExitPos = 3;
9399  }
9400  }
9401  else
9402  {
9403  NextExitPos = 0; // trailing entry point
9404  }
9405  }
9406  else
9407  {
9408  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9409  }
9410  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9411  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9412  int RouteNumber; // holder for referenced value, not used
9413  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9414  {
9415  TimeToAct = -1;
9416  TimeToExit = -1;
9417  }
9418  }
9419  Utilities->CallLogPop(2074);
9420  return(TimeToAct);
9421  }
9422 }
9423 
9424 // ---------------------------------------------------------------------------
9425 
9427 {
9428  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9429  if(LeadElement > -1)
9430  {
9432  {
9433  Utilities->CallLogPop(2148);
9434  return(true);
9435  }
9436  }
9437  if(MidElement > -1)
9438  {
9440  {
9441  Utilities->CallLogPop(2149);
9442  return(true);
9443  }
9444  }
9445  if(LagElement > -1)
9446  {
9448  {
9449  Utilities->CallLogPop(2150);
9450  return(true);
9451  }
9452  }
9453  Utilities->CallLogPop(2151);
9454  return(false);
9455 }
9456 
9457 // ---------------------------------------------------------------------------
9458 // TTrainController
9459 // ---------------------------------------------------------------------------
9460 
9462 {
9463  OnTimeArrivals = 0;
9464  LateArrivals = 0;
9465  EarlyArrivals = 0;
9466  OnTimePasses = 0;
9467  LatePasses = 0;
9468  EarlyPasses = 0;
9469  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9470  LateExits = 0;
9471  EarlyExits = 0;
9472  OnTimeDeps = 0;
9473  LateDeps = 0;
9474  MissedStops = 0;
9475  OtherMissedEvents = 0;
9476  UnexpectedExits = 0;
9477  NumFailures = 0;
9478  IncorrectExits = 0;
9479  SPADEvents = 0;
9480  SPADRisks = 0;
9481  CrashedTrains = 0;
9482  Derailments = 0;
9483  TotArrDepPass = 0;
9484  TotLateArrMins = 0;
9485  TotEarlyArrMins = 0;
9486  TotLatePassMins = 0;
9487  TotEarlyPassMins = 0;
9488  TotLateExitMins = 0; //added at v2.9.1
9489  TotEarlyExitMins = 0; //added at v2.9.1
9490  TotLateDepMins = 0;
9491  ExcessLCDownMins = 0;
9492  SkippedTTEvents = 0; //added at v2.11.0
9493  TTClockTime = 0; // added for v0.6
9495  // added at v1.3.0 to ensure false at start
9496  OpTimeToActUpdateCounter = 0; // new v2.2.0
9497  OpActionPanelVisible = false; // new v2.2.0
9498  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9499  SSHigh = false;
9500  MRSHigh = false;
9501  MRSLow = false;
9502  MassHigh = false;
9503  BFHigh = false;
9504  BFLow = false;
9505  PwrHigh = false;
9506  SigSHigh = false;
9507  SigSLow = false;
9508  randomize();
9509  // to seed rand() & random() with a random number (see UpdateTrain)
9510 }
9511 
9512 // ---------------------------------------------------------------------------
9513 
9515 {
9516  for(unsigned int x = 0; x < TrainVector.size(); x++)
9517  {
9518  TrainVectorAt(32, x).DeleteTrain(4);
9519  }
9520  TrainVector.clear();
9521 }
9522 
9523 // ---------------------------------------------------------------------------
9524 
9525 void TTrainController::LogEvent(AnsiString Str)
9526 {
9527  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9528 
9529  // restrict to last 1000 entries
9530  Utilities->EventLog.push_back(FullStr);
9531  if(Utilities->EventLog.size() > 1000)
9532  {
9533  Utilities->EventLog.pop_front();
9534  }
9535 }
9536 
9537 // ---------------------------------------------------------------------------
9538 
9540 {
9541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9542  bool ClockState = Utilities->Clock2Stopped;
9543  Utilities->Clock2Stopped = true;
9544  // new section dealing with Snt & Snt-sh additions
9545  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9546  // clock tick after stops flashing
9548  {
9549  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9550  {
9551  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9552  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9553  TActionEventType EventType = NoEvent;
9554  if(AVEntry0.Command == "Snt")
9555  {
9556  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9557  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9558  int IncrementalMinutes = 0;
9559  int IncrementalDigits = 0;
9560  if(AVEntryLast.FormatType == Repeat)
9561  {
9562  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9563  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9564  }
9565  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9566  {
9567  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9568  }
9569  // see above note
9570 
9571  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9572  {
9573  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9574  if(TTOD.RunningEntry != NotStarted)
9575  {
9576  continue;
9577  }
9578 
9579 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9580 //if so and no arrival signalled yet bypass the timetabled arrival
9581 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9582 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9583 
9584  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9585  {
9586  break; // all the rest will also be greater
9587  }
9588  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9589  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9590  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9591  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9592  {
9593  TTOD.TrainID = TrainVector.back().TrainID;
9594  TTOD.RunningEntry = Running;
9595  }
9596  else if(EventType == FailTrainEntry)
9597  {
9598  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9599  }
9600  }
9601  }
9602  if(AVEntry0.Command == "Snt-sh")
9603  // just start this once, shuttle repeats take care of restarts
9604  {
9605  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9606  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9607  int IncrementalMinutes = 0;
9608  int IncrementalDigits = 0;
9609  if(AVEntryLast.FormatType == Repeat)
9610  {
9611  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9612  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9613  }
9614  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9615  {
9616  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9617  }
9618  // see above note
9619  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9620  if(TTOD.RunningEntry == NotStarted)
9621  {
9622  if(AVEntry0.EventTime <= TTClockTime)
9623  {
9624  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9625  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9626  TDEntry.SignallerSpeed, false, EventType))
9627  // false for SignallerControl
9628  {
9629  TTOD.TrainID = TrainVector.back().TrainID;
9630  TTOD.RunningEntry = Running;
9631  }
9632  else if(EventType == FailTrainEntry)
9633  {
9634  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9635  }
9636  }
9637  }
9638  }
9639  }
9640  }
9641 
9642  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9643  // iteration, next cycle will catch up with any other pending updates
9644  if(!TrainVector.empty())
9645  {
9646  TrainAdded = false;
9647 
9648 //elapsed time investigations
9649 
9650 //elapsed time segment
9651 //double Start, End;
9652 //AnsiString ElapsedTimeReport = "";
9653 //end elasped time segment
9654  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9655 //elapsed time segment
9656 //PerfLogForm->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
9657 //PerfLogForm->PerformanceLog(-1, "Start time list");
9658 //end elapsed time segment
9659  for(unsigned int x = 0; x < TrainVector.size(); x++)
9660  {
9661 //elapsed time segment
9662 //Start = double(GetTime()) * 86400; //secs
9663 //end elapsed time segment
9664  TrainVectorAt(33, x).UpdateTrain(0);
9665 //elapsed time segment
9666 //End = double(GetTime()) * 86400;
9667 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
9668 //PerfLogForm->PerformanceLog(-1, ElapsedTimeReport);
9669 //end elapsed time segment
9670 
9671 //end elapsed time investigations
9672 
9673  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9674  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9675  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9676  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9677  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9678  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9679  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9680 
9681  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9682  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9683  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9684 
9685  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9686  */
9687  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9688  {
9689  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9690  }
9691  }
9692  // set warning flags
9693  CrashWarning = false;
9694  DerailWarning = false;
9695  SPADWarning = false;
9696  CallOnWarning = false;
9697  SignalStopWarning = false;
9698  BufferAttentionWarning = false;
9699  TrainFailedWarning = false;
9700  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9701  {
9702  TTrain &Train = TrainVectorAt(34, x);
9703  if(Train.Crashed)
9704  // can't use background colours for crashed & derailed because same colour
9705  {
9706  CrashWarning = true;
9707  }
9708  else if(Train.Derailed)
9709  // can't use background colours for crashed & derailed because same colour
9710  {
9711  DerailWarning = true;
9712  }
9713  else if(Train.BackgroundColour == clSPADBackground)
9714  // use colour as that changes as soon as passes signal
9715  {
9716  SPADWarning = true;
9717  }
9718  else if(Train.BackgroundColour == clTrainFailedBackground)
9719  {
9720  TrainFailedWarning = true;
9721  }
9722  else if(Train.BackgroundColour == clCallOnBackground)
9723  // use colour as also stopped at signal
9724  {
9725  CallOnWarning = true;
9726  }
9727  else if(Train.BackgroundColour == clSignalStopBackground)
9728  // use colour to distinguish from call-on
9729  {
9730  SignalStopWarning = true;
9731  }
9732  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9733  // use colour to distinguish from ordinary buffer stop
9734  {
9735  BufferAttentionWarning = true;
9736  }
9737  if(Train.HasTrainGone())
9738  {
9739  AnsiString Loc = "";
9740  bool ElementFound = false;
9741  TTrackElement TE;
9742  if(Train.LagElement > -1)
9743  {
9744  TE = Track->TrackElementAt(531, Train.LagElement);
9745  ElementFound = true;
9746  }
9747  else if(Train.MidElement > -1)
9748  {
9749  TE = Track->TrackElementAt(779, Train.MidElement);
9750  ElementFound = true;
9751  }
9752  else if(Train.LeadElement > -1)
9753  {
9754  TE = Track->TrackElementAt(780, Train.LeadElement);
9755  ElementFound = true;
9756  }
9757  if(ElementFound)
9758  {
9759  if(TE.ActiveTrackElementName != "")
9760  {
9761  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9762  }
9763  else
9764  {
9765  Loc = "track element " + TE.ElementID;
9766  }
9767  }
9768  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9769  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9770  // need above first because may also have ActionVectorEntryPtr == "Fer"
9771  {
9772  Train.UnplotTrain(9);
9773  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9775  {
9778  }
9779  // end of addition
9780  AllRoutes->RebuildRailwayFlag = true;
9781  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9782  // correctly after a crash
9783  }
9784  else if(AVEntryPtr->Command == "Fer")
9785  {
9786  bool CorrectExit = false;
9787  if(!AVEntryPtr->ExitList.empty())
9788  {
9789  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9790  {
9791  if(*ELIT == Train.LagElement)
9792  {
9793  CorrectExit = true;
9794  }
9795  }
9796  }
9797  if(CorrectExit)
9798  {
9799  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
9800  }
9801  else
9802  {
9803  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9804  }
9805  }
9806  else
9807  {
9808  if(!AVEntryPtr->SignallerControl)
9809  {
9810  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9811  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9812  // -2 is marker for send messages for all remaining actions except Fer if present
9813  }
9814  else
9815  {
9816  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
9817  }
9818  }
9819  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
9820  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9821  Train.DeleteTrain(1);
9822  TrainVector.erase(TrainVector.begin() + x);
9823  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
9824  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
9825  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
9826  }
9827  }
9828  }
9829  else
9830  {
9831  // reset all flags in case last train removed with flag set
9832  CrashWarning = false;
9833  DerailWarning = false;
9834  SPADWarning = false;
9835  CallOnWarning = false;
9836  SignalStopWarning = false;
9837  BufferAttentionWarning = false;
9838  TrainFailedWarning = false;
9839  }
9840  // update OpTimeToActMultimap
9842  {
9844  // clears entries then adds values for running trains then for continuation entries
9846  //added for multiplayer for running trains only
9847  }
9848  Utilities->Clock2Stopped = ClockState;
9849  Utilities->CallLogPop(723);
9850 }
9851 
9852 // ---------------------------------------------------------------------------
9854 {
9855  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9856  if(!TrainVector.empty())
9857  {
9858  for(int x = TrainVector.size() - 1; x >= 0; x--)
9859  {
9860  TrainVectorAt(50, x).DeleteTrain(2);
9861  }
9862  TrainVector.clear();
9863  }
9864  if(!TrainDataVector.empty())
9865  {
9866  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9867  {
9868  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9869  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9870  {
9871  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9872  TOD.RunningEntry = NotStarted;
9873  TOD.TrainID = -1;
9874  TOD.EventReported = NoEvent;
9875  }
9876  }
9877  }
9878  Display->GetOutputLog1()->Caption = "";
9879  Display->GetOutputLog2()->Caption = "";
9880  Display->GetOutputLog3()->Caption = "";
9881  Display->GetOutputLog4()->Caption = "";
9882  Display->GetOutputLog5()->Caption = "";
9883  Display->GetOutputLog6()->Caption = "";
9884  Display->GetOutputLog7()->Caption = "";
9885  Display->GetOutputLog8()->Caption = "";
9886  Display->GetOutputLog9()->Caption = "";
9887  Display->GetOutputLog10()->Caption = "";
9888  Utilities->CallLogPop(1352);
9889 }
9890 
9891 // ---------------------------------------------------------------------------
9892 
9894 {
9895  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9896  if(!TrainVector.empty())
9897  {
9898  for(unsigned int x = 0; x < TrainVector.size(); x++)
9899  {
9900  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
9901  { //see Kevin Smith error information for details
9902  TrainVectorAt(51, x).PlotTrain(4, Disp);
9903  }
9904  }
9905  }
9906  Utilities->CallLogPop(724);
9907 }
9908 
9909 // ---------------------------------------------------------------------------
9910 
9911 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9912 {
9913  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9914  if(!TrainVector.empty())
9915  {
9916  for(unsigned int x = 0; x < TrainVector.size(); x++)
9917  {
9918  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9919  }
9920  }
9921  Utilities->CallLogPop(1707);
9922 }
9923 
9924 // ---------------------------------------------------------------------------
9925 
9927 {
9928  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9929  if(!TrainVector.empty())
9930  {
9931  for(unsigned int x = 0; x < TrainVector.size(); x++)
9932  {
9933  TrainVectorAt(52, x).UnplotTrain(10);
9934  }
9935  }
9937  Utilities->CallLogPop(725);
9938 }
9939 
9940 // ---------------------------------------------------------------------------
9941 
9942 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9943  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9944  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9945 {
9946  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9947  "," + AnsiString(Mass) + "," + ModeStr);
9948  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9949  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
9950 
9951  int RearExitPos = -1;
9952 
9953  for(int x = 0; x < 4; x++)
9954  {
9955  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9956  {
9957  RearExitPos = x;
9958  }
9959  }
9960  if(RearExitPos == -1)
9961  {
9962  throw Exception("Error, RearExit == -1 in AddTrain");
9963  }
9964  bool ReportFlag = true;
9965 
9966  // used to stop repeated messages from CheckStartAllowable when split failed
9967  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9968  {
9969  ReportFlag = false;
9970  }
9971  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9972  {
9973  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9974  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9975  Utilities->CallLogPop(938);
9976  return(false);
9977  }
9978  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9979  TTrainMode TrainMode = NoMode;
9980 
9981  if(ModeStr == "Timetable")
9982  {
9983  TrainMode = Timetable;
9984  }
9985  // all else gives 'None', 'Signaller' set within program
9986 
9987  if(MaxRunningSpeed < 10)
9988  {
9989  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9990  }
9991  if(SignallerSpeed < 10)
9992  {
9993  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9994  }
9995  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9996  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9997 
9998  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
9999 
10000  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
10001  // initialise here rather than in TTrain constructor as create trains
10002  // with Null TrainDataEntryPtr when loading session trains
10003  if(SignallerControl)
10004  {
10005  NewTrain->TimetableFinished = true;
10006  NewTrain->SignallerStoppingFlag = false;
10007  NewTrain->TrainMode = Signaller;
10008  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
10009  {
10010  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
10011  }
10013  }
10014  // deal with starting conditions:-
10015  // unlocated Snt: just report entry & advance pointer
10016  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
10017  // Sns doesn't need a new train
10018  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
10019  // covers all above located starts
10020  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
10021  // wouldn't have accepted the timetable
10022  {
10023  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
10024  // StoppedAtBuffers is set in UpdateTrain()
10025  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
10026  // buffer end must be ahead of train or would have failed start position check
10027  {
10028  NewTrain->StoppedAtLocation = true;
10029  NewTrain->PlotStartPosition(0);
10031  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
10032  NewTrain->ActionVectorEntryPtr->Warning);
10033  if(!SignallerControl) // don't advance if SignalControlEntry
10034  {
10035  NewTrain->ActionVectorEntryPtr++;
10036  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
10037  }
10038  NewTrain->LastActionTime = TTClockTime;
10039  }
10040  // else a through station stop
10041  else
10042  {
10043  NewTrain->StoppedAtLocation = true;
10044  NewTrain->PlotStartPosition(10);
10046  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
10047  NewTrain->ActionVectorEntryPtr->Warning);
10048  if(!SignallerControl) // don't advance if SignalControlEntry
10049  {
10050  NewTrain->ActionVectorEntryPtr++;
10051  }
10052  NewTrain->LastActionTime = TTClockTime;
10053  }
10054  }
10055  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
10056  {
10057  NewTrain->PlotStartPosition(11);
10058  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
10059  AnsiString Loc = "";
10060  if(TE.ActiveTrackElementName != "")
10061  {
10062  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10063  }
10064  else
10065  {
10066  Loc = "track element " + TE.ElementID;
10067  }
10068  if(TE.TrackType == Continuation)
10069  {
10070  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10071  }
10072  else
10073  {
10074  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10075  }
10076  if(!SignallerControl) // don't advance if SignalControlEntry
10077  {
10078  NewTrain->ActionVectorEntryPtr++;
10079  }
10080  NewTrain->LastActionTime = TTClockTime;
10081  // no need to set LastActionTime for an unlocated entry
10082  }
10083  // cancel a wrong-direction route if either element of train starts on one
10084  if(NewTrain->LeadElement > -1)
10085  {
10086  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
10087  }
10088  if(NewTrain->MidElement > -1)
10089  {
10090  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
10091  }
10092  // set signals for a right-direction autosigs route for either element of train on one
10093  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
10094  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
10095  int RouteNumber = -1;
10096  bool SignalsSet = false;
10097 
10098  if(NewTrain->LeadElement > -1)
10099  {
10100  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10101  {
10102  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10103  int RouteStartPosition;
10104  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10105  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
10106  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
10107  if(FirstPair.first == RouteNumber)
10108  {
10109  RouteStartPosition = FirstPair.second;
10110  }
10111  else if(SecondPair.first == RouteNumber)
10112  {
10113  RouteStartPosition = SecondPair.second;
10114  }
10115  else
10116  {
10117  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
10118  }
10119  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
10120  SignalsSet = true;
10121  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10122  }
10123  else if(RouteNumber > -1) // non-autosigsroute
10124  {
10125  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
10126  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10127  int FirstELinkPos = TempPDE.GetELinkPos();
10128  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
10129  {
10130  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10131  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
10132  }
10133  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
10134  {
10135  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10136  // remove the last element under LeadElement
10137  }
10138  AllRoutes->RebuildRailwayFlag = true;
10139  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10140  // now deal with a rear linked autosigs route
10141  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
10142  {
10143  int LinkedRouteNumber = -1;
10144  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
10145  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10146  {
10147  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
10148  // this is ok as here we are setting signals from the start of the route
10149  }
10150  }
10151  SignalsSet = true;
10152  }
10153  }
10154  if(NewTrain->MidElement > -1)
10155  // if entering at a continuation MidElement == -1
10156  {
10157  // this is included in case a train starts with LeadElement on no route and MidElement on a route
10158  if(!SignalsSet)
10159  {
10160  RouteNumber = -1;
10161  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10162  {
10163  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10164  int RouteStartPosition;
10165  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10166  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
10167  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
10168  if(FirstPair.first == RouteNumber)
10169  {
10170  RouteStartPosition = FirstPair.second;
10171  }
10172  else if(SecondPair.first == RouteNumber)
10173  {
10174  RouteStartPosition = SecondPair.second;
10175  }
10176  else
10177  {
10178  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
10179  }
10180  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
10181  SignalsSet = true;
10182  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10183  }
10184  else if(RouteNumber > -1) // non-autosigsroute
10185  {
10186  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
10187  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10188  int FirstELinkPos = TempPDE.GetELinkPos();
10189  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
10190  {
10191  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10192  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
10193  }
10194  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
10195  {
10196  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10197  // remove the last element under LeadElement
10198  }
10199  AllRoutes->RebuildRailwayFlag = true;
10200  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10201  // now deal with a rear linked autosigs route
10202  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
10203  {
10204  int LinkedRouteNumber = -1;
10205  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
10206  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10207  {
10208  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
10209  // this is ok as now we are setting signals from the start of the route
10210  }
10211  }
10212  }
10213  }
10214  }
10215  TrainVector.push_back(*NewTrain);
10216  Utilities->CallLogPop(731);
10217  return(true);
10218 }
10219 
10220 // ---------------------------------------------------------------------------
10221 
10222 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
10223 {
10224  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
10225  AnsiString(TrackVectorNumber));
10226  int VecPos = -1;
10227 
10228  for(unsigned int x = 0; x < TrainVector.size(); x++)
10229  {
10230  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
10231  {
10232  VecPos = x;
10233  }
10234  }
10235  if(VecPos == -1)
10236  {
10237  throw Exception("Error, VecPos not set in EntryPos");
10238  }
10239  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
10240  {
10241  Utilities->CallLogPop(734);
10242  return(TrainVectorAt(3, VecPos).LeadEntryPos);
10243  }
10244  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
10245  {
10246  Utilities->CallLogPop(735);
10247  return(TrainVectorAt(5, VecPos).MidEntryPos);
10248  }
10249  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
10250  {
10251  Utilities->CallLogPop(736);
10252  return(TrainVectorAt(7, VecPos).LagEntryPos);
10253  }
10254  Utilities->CallLogPop(737);
10255  return(-1);
10256 }
10257 
10258 // ---------------------------------------------------------------------------
10259 
10261 {
10262  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
10263  for(unsigned int x = 0; x < TrainVector.size(); x++)
10264  {
10265  if(TrainVectorAt(53, x).TrainID == TrainID)
10266  {
10267  Utilities->CallLogPop(738);
10268  return(TrainVectorAt(54, x));
10269  }
10270  }
10271  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
10272 }
10273 
10274 // ---------------------------------------------------------------------------
10275 
10276 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
10277 // return true if find the train (added at v2.4.0 as can select a removed train in
10278 // ActionsDueListBox before it updates - reported by LiWinDom in error report 23/04/20)
10279 {
10280  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
10281  for(unsigned int x = 0; x < TrainVector.size(); x++)
10282  {
10283  if(TrainVectorAt(69, x).TrainID == TrainID)
10284  {
10285  Utilities->CallLogPop(2152);
10286  return(true);
10287  }
10288  }
10289  Utilities->CallLogPop(2153);
10290  return(false);
10291 }
10292 
10293 // ---------------------------------------------------------------------------
10294 
10295 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
10296 {
10297  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10298  Utilities->Format96HHMMSS(Time));
10299  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10300 
10301  Utilities->CallLogPop(2061);
10302  return(RepeatTime);
10303 }
10304 
10305 // ---------------------------------------------------------------------------
10306 
10307 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10308 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10309 {
10310  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10311  AnsiString RetStr = "", PartStr = "";
10312  int Count = 0;
10313  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10314 
10315  Ptr--; // because incremented at start of loop
10316  do
10317  {
10318  Ptr++;
10319  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10320  {
10321  continue; // move past the starting entry
10322  }
10323  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10324  {
10325  break;
10326  }
10327  if(Ptr->SignallerControl)
10328  {
10329  RetStr = "Train under signaller control";
10330  break;
10331  }
10332  if(Ptr->FormatType == TimeTimeLoc)
10333  {
10334  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10335  {
10336  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10337  }
10338  else
10339  {
10340  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10341  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10342  Count++; // because there are 2 entries
10343  }
10344  }
10345  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10346  {
10347  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10348  }
10349  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10350  {
10351  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10352  }
10353  else if(Ptr->FormatType == PassTime) // new
10354  {
10355  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10356  }
10357  else if(Ptr->Command == "Fns")
10358  {
10359  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10360  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10361  PartStr = ControllerGetNewServiceDepartureInfo(11, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
10362  }
10363  else if(Ptr->Command == "F-nshs")
10364  {
10365  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10366  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10367  PartStr = ControllerGetNewServiceDepartureInfo(13, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10368  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10369  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10370  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10371  }
10372 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10373  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10374  {
10375  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10376  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10377  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10378  PartStr = ControllerGetNewServiceDepartureInfo(15, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10379  }
10380  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10381  {
10382  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10383  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10384  PartStr = ControllerGetNewServiceDepartureInfo(17, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10385  }
10386  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10387  {
10388  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10389  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10390  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10391  PartStr = ControllerGetNewServiceDepartureInfo(19, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10392  }
10393  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10394  {
10395  PartStr = "Terminate at " + Ptr->LocationName;
10396  }
10397  else if(Ptr->Command == "Frh")
10398  {
10399  PartStr = "Terminate at " + Ptr->LocationName;
10400  }
10401  else if(Ptr->Command == "Fer")
10402  {
10403  AnsiString AllowedExits;
10404  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10405  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10406  }
10407  else if(Ptr->Command == "Fjo")
10408  {
10409  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10410  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10411  }
10412  else if(Ptr->Command == "jbo")
10413  {
10414  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10415  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10416  }
10417  else if(Ptr->Command == "fsp")
10418  {
10419  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10420  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10421  }
10422  else if(Ptr->Command == "rsp")
10423  {
10424  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10425  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10426  }
10427  else if(Ptr->Command == "cdt")
10428  {
10429  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10430  }
10431  if(RetStr != "")
10432  {
10433  RetStr = RetStr + '\n' + PartStr;
10434  }
10435  else
10436  {
10437  RetStr = PartStr;
10438  }
10439  Count++;
10440  }
10441  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10442  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10443  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10444  // forward as anyone should wish to see without looking at the full timetable
10445  Utilities->CallLogPop(2072);
10446  return(RetStr);
10447 }
10448 
10449 // ---------------------------------------------------------------------------
10450 
10451 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10452 {
10453  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
10454  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10455  AnsiString DepTime = "", EventTime = "";
10456  bool CDTFlag = false; //reports if train changes direction before departs
10457  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10458  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10459  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10460  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10461  {
10462  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10463  {
10464  TowardsLocation = AVI->LocationName;
10465  }
10466  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10467  {
10468  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10469  if(TE.ActiveTrackElementName != "")
10470  {
10471  TowardsLocation = TE.ActiveTrackElementName;
10472  }
10473  else
10474  {
10475  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10476  }
10477  }
10478  }
10479  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10480  {
10481  if(AVI->Command == "cdt")
10482  {
10483  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10484  continue;
10485  }
10486  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10487  {
10488  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10489  RetStr += "\nNew service splits at " + EventTime;
10490  Utilities->CallLogPop(2237);
10491  return(RetStr);
10492  }
10493  if(AVI->Command == "jbo")
10494  {
10495  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10496  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10497  Utilities->CallLogPop(2238);
10498  return(RetStr);
10499  }
10500  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10501  {
10502  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10503  if(CDTFlag)
10504  {
10505  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10506  {
10507  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10508  }
10509  else
10510  {
10511  RetStr += "\nNew service changes direction then departs at " + DepTime;
10512  }
10513  }
10514  else
10515  {
10516  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10517  {
10518  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10519  }
10520  else
10521  {
10522  RetStr += "\nNew service departs at " + DepTime;
10523  }
10524  }
10525  Utilities->CallLogPop(2239);
10526  return(RetStr);
10527  }
10528  }
10529  Utilities->CallLogPop(2223);
10530  return(RetStr);
10531 }
10532 
10533 // ---------------------------------------------------------------------------
10534 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10535 /*
10536  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10537  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10538  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10539 
10540  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10541  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
10542  user wishes
10543 
10544  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10545  descriptive text or anything user wishes
10546  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10547  be ignored) is taken as the timetable start time.
10548  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10549  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10550  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10551  within the timetable if required.
10552  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10553  services)
10554  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10555  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10556 
10557  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10558  text timetable file easier
10559 
10560  form:-
10561  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10562  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10563  then multiple entries, separated by commas, of the form:-
10564 
10565  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10566  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
10567  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
10568 
10569  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
10570  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
10571  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
10572 
10573  HH:MM;Command (cdt) }TimeCmd }
10574  HH:MM;Location (arr & dep) }TimeLoc }
10575  HH:MM;HH:MM;Location }TimeTimeLoc }
10576  HH:MM;pas;Location }PassTime }
10577  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
10578  HH:MM;Fer;set of allowable IDs }ExitRailway }
10579  Command (Frh only) }FinRemHere }
10580 
10581  R;mm;dd;nn. Repeat Repeat entry
10582 
10583  Formats:
10584 
10585  Command only: Frh
10586  Time;Command: cdt
10587  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
10588  Time;Command;2 Element IDs: Snt
10589  Time;Comand;n Element IDs: Fer
10590  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
10591  Time;Command;2 Element IDs;Headcode Snt-sh
10592  Time;Command;Location pas
10593  Time;Location Arr Dep
10594  Time;Time;Location Arr & dep together
10595 
10596  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
10597 
10598  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
10599  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
10600  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
10601 
10602  4 2x Linked entries, all shuttles:
10603 
10604  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
10605  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
10606  -> Remain Here (at finish location after all repeats)
10607  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
10608  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
10609 
10610  Allowable successors:-
10611 
10612  Successor state Type
10613 
10614  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
10615  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
10616  Sfs AtLoc )
10617  Sns AtLoc ) Start
10618  Sns-fsh AtLoc )
10619  Snt-sh AtLoc )
10620  Sns-sh AtLoc )
10621 
10622  pas Moving )
10623  jbo AtLoc )
10624  fsp AtLoc )
10625  rsp AtLoc ) Intermediate
10626  cdt AtLoc )
10627  TimeLoc arr Moving (bef), AtLoc (aft) )
10628  TimeLoc dep AtLoc (bef), Moving (aft) )
10629  TimeTimeLoc Moving )
10630 
10631  Fns Repeat/Nothing)
10632  Fjo Repeat/Nothing)
10633  Frh Repeat/Nothing)
10634  Fer Repeat/Nothing) Finish
10635  Frh-sh Repeat )
10636  Fns-sh Repeat )
10637  F-nshs Nothing )
10638 
10639  Descriptions:
10640  Snt New train
10641  Sfs New service from split
10642  Sns New service from another service
10643  Sns-fsh New non-repeating service from a shuttle service
10644  Snt-sh New shuttle train at a timetabled stop
10645  Sns-sh New shuttle service from a feeder service
10646 
10647  pas Pass
10648  jbo Be joined by another train
10649  fsp Front split
10650  rsp Rear split
10651  cdt Change direction of train
10652  TimeLoc arr Arrival
10653  TimeLoc dep Departure
10654  TimeTimeLoc Arrival and departure
10655 
10656  Fns Finish & form a new service
10657  Fjo Finish & join another train
10658  Frh Finish & remain here
10659  Fer Finish & exit railway
10660  Frh-sh Finish & repeat shuttle, finally remain here
10661  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10662  F-nshs Finish & form a shuttle feeder service
10663 */
10664 
10665 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10666 {
10667  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10668  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10669  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10670  // new for v0.2b
10671  // compile ActiveTrackElementNameMap
10672  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10673 
10675  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10676  {
10677  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10679  == Track->ContinuationNameMap.end())
10680  {
10681  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10682  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10683  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10684  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10685  }
10686  }
10688  // end of new section
10689  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10690 
10691  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10692  if(TTBLFile.is_open())
10693  {
10694  char *TrainTimetableString = new char[10000];
10695  // enough for over 200 stations, should be adequate!
10696  bool EndOfFile = false;
10697  int Count = 0;
10698  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10699  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10700  // delimiter is '\0' as it's an AnsiString
10701  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10702  // file empty - stores a null in 1st position if doesn't load any characters
10703  {
10704  // may still have eof even if read a line (no CRLF at end), and
10705  // if so need to process it
10706  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10707  TTBLFile.close();
10708  delete[] TrainTimetableString;
10709  Utilities->CallLogPop(1611);
10710  return(false);
10711  }
10712  AnsiString OneLine(TrainTimetableString);
10713  bool FinalCallFalse = false;
10714  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10715  // get rid of lines before the start time
10716  {
10717  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10718  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10719  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10720  // stores a null in 1st position if doesn't load any characters
10721  {
10722  // may still have eof even if read a line (no CRLF at end), and
10723  // if so need to process it
10724  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10725  TTBLFile.close();
10726  delete[] TrainTimetableString;
10727  Utilities->CallLogPop(772);
10728  return(false);
10729  }
10730  OneLine = AnsiString(TrainTimetableString);
10731  }
10732  // here when have accepted the start time
10733  Count++; // increment past the start time
10734  while(!EndOfFile)
10735  {
10736  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10737  // get next line after start time
10738  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10739  // stores a null in 1st position if doesn't load any characters
10740  {
10741  // may still have eof even if read a line (no CRLF at end), and
10742  // if so need to process it
10743  EndOfFile = true;
10744  OneLine = "";
10745  }
10746  else
10747  {
10748  OneLine = AnsiString(TrainTimetableString);
10749  }
10750  if(OneLine.Length() > 9999)
10751  {
10752  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10753  TTBLFile.close();
10754  delete[] TrainTimetableString;
10755  Utilities->CallLogPop(789);
10756  return(false);
10757  }
10758  bool FinalCallFalse = false;
10759  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10760  // false for FinalCall - just checking at this stage
10761  {
10762  TTBLFile.close();
10763  delete[] TrainTimetableString;
10764  Utilities->CallLogPop(770);
10765  return(false);
10766  }
10767  if(EndOfFile && (Count < 2))
10768  // Timetable must contain at least two relevant lines, one for start time and at least one train
10769  {
10770  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10771  TTBLFile.close();
10772  delete[] TrainTimetableString;
10773  Utilities->CallLogPop(771);
10774  return(false);
10775  }
10776  Count++;
10777  }
10778  delete[] TrainTimetableString;
10779  TTBLFile.close();
10780  } // if(TTBLFile.is_open())
10781  else
10782  {
10783  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
10784  Utilities->CallLogPop(2154);
10785  return(false);
10786  }
10787  Utilities->CallLogPop(753);
10788  return(true);
10789 }
10790 
10791 // ---------------------------------------------------------------------------
10792 
10793 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
10794  bool CheckLocationsExistInRailway) // return true for success
10795 
10796 /* Format:
10797  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10798  descriptive text or anything user wishes
10799  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10800  be ignored) is taken as the timetable start time.
10801  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10802  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10803  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10804  within the timetable if required.
10805  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10806  services)
10807  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10808  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10809 
10810  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10811  text timetable file easier
10812 
10813  form:-
10814  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10815  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10816  then multiple entries, separated by commas, of the form:-
10817 
10818  Format FormatType
10819  [W]HH:MM;Command (cdt) }TimeCmd }
10820  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
10821  [W]HH:MM;pas;Location }PassTime }
10822  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10823  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
10824  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
10825  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
10826  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
10827  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
10828  [W]HH:MM;Fns-sh;Details }FSHNewService }
10829  [W]HH:MM;Location (arr & dep) }TimeLoc }
10830  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
10831  Command (Frh only) }FinRemHere }
10832 
10833  R;mm;dd;nn. Repeat Repeat entry
10834 
10835  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
10836  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
10837  at location; or (c) departure time if train already at location (including train started at location either as a new
10838  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
10839  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
10840  minutes, incremental train headcode last 2 digits, and number of repeats.
10841 
10842  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
10843  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
10844  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
10845  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
10846  (it's for a shuttle train to return to depot at end of services)
10847 
10848  Command/Location & details are as follows:-
10849 
10850  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
10851  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
10852  2E44 in its Sfs entry. All these are checked.
10853  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
10854 
10855  Start commands:-
10856  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
10857  with loc as a start entry can't have a location as details)
10858  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
10859  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
10860  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
10861  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
10862  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
10863  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
10864  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
10865  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
10866  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
10867  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
10868  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
10869 
10870  Intermediate commands:-
10871  Time - Location (TimeLoc), can be arrival or departure depending on context
10872  Time Time location (TimeTimeLoc), arrival and departure
10873  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
10874  pas (PassTime), Time;pas;Location
10875  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
10876  joining train's finish details must correspond or the file check will fail
10877  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
10878  new train - that train's starting information must correspond)
10879  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
10880  new train - that train's starting information must correspond)
10881  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
10882 
10883  Finish commands:-
10884  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
10885  creation)
10886  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
10887  shuttle headcode (no train creation)
10888  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10889  may have to wait for it), details = new headcode (delete train)
10890  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10891  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10892  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10893  here
10894  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10895  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10896 
10897  Repeat:-
10898  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10899  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10900 
10901  Checks carried out with error messages in this function:-
10902  At least one comma in a service line (it's based on a .csv file)
10903  No entries following train information;
10904  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10905  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10906  First entry not a start entry;
10907  Train information incomplete before a start entry;
10908  Entry follows a finish entry but doesn't begin with 'R';
10909  SplitEntry returns false in a finish entry - message repeats the entry for information;
10910  Last action entry isn't a finish entry.
10911 
10912  Function returns false with no message if:-
10913  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10914  time is found at all then an error message is given in the calling function);
10915  SplitTrainInfo returns false (message given in called function);
10916  SplitRepeat returns false (message given in called function).
10917 */{
10918  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10919  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10920  TTrainDataEntry TempTrainDataEntry;
10921 
10922  EndOfFile = false;
10923  StripSpaces(0, OneLine);
10924  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10925  // semicolons within the line
10926  ServiceReference = "";
10927  if(OneLine != "")
10928  {
10929  if(OneLine[1] != '*')
10930  {
10931  int SCPos = OneLine.Pos(';');
10932  if(SCPos == 0)
10933  {
10934  ServiceReference = OneLine.SubString(1, 8);
10935  }
10936  else
10937  {
10938  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10939  }
10940  }
10941  }
10942  bool AllCommas = true;
10943 
10944  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10945  {
10946  if(OneLine[x] != ',')
10947  {
10948  AllCommas = false;
10949  }
10950  }
10951  if(AllCommas || (OneLine == ""))
10952  {
10953  if(Count > 0)
10954  {
10955  EndOfFile = true;
10956  // returns true for a blank line - treated as end of file
10957  Utilities->CallLogPop(1018);
10958  return(true);
10959  }
10960  else // count == 0 so not yet found a start time, no message to be given
10961  {
10962  Utilities->CallLogPop(754);
10963  return(false);
10964  }
10965  }
10966  AnsiString First = "", Second = "", Third = "", Fourth = "";
10967  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10968  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10969  TDateTime StartTime(0);
10970  TNumList ExitList;
10971  bool Warning = false;
10972 
10973  if(Count == 0) // no start time found yet
10974  {
10975 /* dropped at v0.6b
10976  AnyHeadCodeValid = false;
10977  if(OneLine.SubString(6,5) == ";0000")
10978  {
10979  AnyHeadCodeValid = true;
10980  }
10981 */
10982  if(!CheckTimeValidity(0, OneLine, StartTime))
10983  {
10984  // no message is given for an invalid time as it's assumed to be an irrelevant line
10985  // if no start time is found at all then an error message is given in the calling function
10986  // AnyHeadCodeValid = false;
10987  Utilities->CallLogPop(755);
10988  return(false);
10989  }
10990  if(FinalCall) // here if start time valid
10991  {
10992  TTClockTime = StartTime;
10993  TimetableStartTime = StartTime;
10994  }
10995  }
10996  else
10997  {
10998  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10999  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
11000  double MaxBrakeRate = 0;
11001  double PowerAtRail = 0;
11002  int SignallerSpeed = 0;
11003  if(OneLine[1] == '*')
11004  {
11005  Utilities->CallLogPop(1581);
11006  return(true);
11007  // ignore any line beginning with '*' but return true as there is no error
11008  }
11009  int Pos = OneLine.Pos(',');
11010  if(Pos == 0)
11011  {
11012  int SubStringLength = 20;
11013  if(OneLine.Length() < 20)
11014  {
11015  SubStringLength = OneLine.Length();
11016  }
11017  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
11018  Utilities->CallLogPop(766);
11019  return(false);
11020  }
11021  TrainInfoStr = OneLine.SubString(1, Pos - 1);
11022  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
11023  GiveMessages)) // error messages given in SplitTrainInfo
11024  {
11025  Utilities->CallLogPop(773);
11026  return(false);
11027  }
11028  if(FinalCall)
11029  {
11030  // store Train info - conversions done in SplitTrainInfo
11031  // only headcode mandatory for continuing services
11032  TempTrainDataEntry.HeadCode = HeadCode;
11033  TempTrainDataEntry.ServiceReference = HeadCode;
11034  TempTrainDataEntry.Description = Description;
11035  TempTrainDataEntry.StartSpeed = StartSpeed;
11036  TempTrainDataEntry.Mass = Mass;
11037  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
11038  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
11039  TempTrainDataEntry.PowerAtRail = PowerAtRail;
11040  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
11041  TTrainOperatingData TempTrainOperatingData;
11042  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
11043  }
11044  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
11045  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
11046  // so strip them off
11047  while(NewRemainder[NewRemainder.Length()] == ',')
11048  {
11049  if(NewRemainder.Length() > 1)
11050  {
11051  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
11052  }
11053  else
11054  {
11055  NewRemainder = "";
11056  break;
11057  }
11058  }
11059  // check if zero length & fail if so
11060  if(NewRemainder == "")
11061  {
11062  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
11063  Utilities->CallLogPop(769);
11064  return(false);
11065  }
11066  // now have one more entry than there are commas
11067  int CommaCount = 0;
11068  for(int x = 1; x < NewRemainder.Length() + 1; x++)
11069  {
11070  if(NewRemainder[x] == ',')
11071  {
11072  CommaCount++;
11073  }
11074  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
11075  if(CommaCount == 0)
11076  {
11077  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
11078  {
11079  int SubStringLength = 20;
11080  if(OneLine.Length() < 20)
11081  {
11082  SubStringLength = OneLine.Length();
11083  }
11084  TimetableMessage(GiveMessages,
11085  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
11086  OneLine.SubString(1, SubStringLength) + "'....");
11087  Utilities->CallLogPop(783);
11088  return(false);
11089  }
11090  }
11091  AnsiString OneEntry = "";
11092  TTimetableFormatType FormatType;
11093  TTimetableSequenceType SequenceType;
11094  TTimetableLocationType LocationType;
11095  TTimetableShuttleLinkType ShuttleLinkType;
11096  bool FinishFlag = false;
11097  for(int x = 0; x < CommaCount + 1; x++)
11098  {
11099  if((CommaCount == 0) || (x < CommaCount))
11100  // i.e. train entered under signaller control with no repeats, or entry is not the last,
11101  // in which case there's a comma & finish element or repeat still to come this entry could
11102  // be a finish but can't be a repeat
11103  {
11104  if(CommaCount == 0)
11105  {
11106  OneEntry = NewRemainder;
11107  NewRemainder = "";
11108  }
11109  else
11110  {
11111  Pos = NewRemainder.Pos(',');
11112  OneEntry = NewRemainder.SubString(1, Pos - 1);
11113  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
11114  }
11115  First = "";
11116  Second = "";
11117  Third = "";
11118  Fourth = "";
11119  RearStartOrRepeatMins = 0;
11120  FrontStartOrRepeatDigits = 0;
11121  NumberOfRepeats = 0;
11122  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11123  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11124  {
11125  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11126  Utilities->CallLogPop(756);
11127  return(false);
11128  }
11129  // check if warning for Frh or Fjo & reject
11130  if(Warning && (Second == "Frh"))
11131  {
11132  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
11133  Utilities->CallLogPop(1793);
11134  return(false);
11135  }
11136  if(Warning && (Second == "Fjo"))
11137  {
11138  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11139  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
11140  Utilities->CallLogPop(1794);
11141  return(false);
11142  }
11143  if(x == 0) // should be start event
11144  {
11145  if(SequenceType != Start)
11146  {
11147  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
11148  Utilities->CallLogPop(784);
11149  return(false);
11150  }
11151  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
11152  {
11153  if(NewRemainder[1] != 'R')
11154  {
11155  TimetableMessage(GiveMessages,
11156  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
11157  OneEntry + "'");
11158  Utilities->CallLogPop(787);
11159  return(false);
11160  }
11161  }
11162  if((Second == "Snt") || (Second == "Snt-sh"))
11163  // need full train information including non-default values for at least HeadCode, Description,
11164  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
11165  {
11166  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
11167  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
11168  {
11169  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
11170  OneEntry + "'");
11171  Utilities->CallLogPop(1783);
11172  return(false);
11173  }
11174  }
11175  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
11176  // service continuation - need at least non-default value for HeadCode
11177  {
11178  if(HeadCode == "")
11179  {
11180  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11181  OneEntry + "'");
11182  Utilities->CallLogPop(788);
11183  return(false);
11184  }
11185  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
11186  {
11187  TimetableMessage(GiveMessages,
11188  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11189  OneEntry + "'");
11190  Utilities->CallLogPop(843);
11191  return(false);
11192  }
11193  }
11194  }
11195  if(SequenceType == Finish)
11196  {
11197  FinishFlag = true;
11198  // marker for only permitted additional entry being a repeat, only needed if the
11199  // finish entry is not the last entry
11200  }
11201  if(FinalCall)
11202  {
11203  // interpret & add to ActionVector
11204  TDateTime TempTime;
11205  TActionVectorEntry ActionVectorEntry;
11206  ActionVectorEntry.FormatType = FormatType;
11207  ActionVectorEntry.LocationType = LocationType;
11208  ActionVectorEntry.SequenceType = SequenceType;
11209  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11210  ActionVectorEntry.Warning = Warning;
11211  if(FormatType == TimeLoc)
11212  {
11213  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
11214  {
11215  ;
11216  } // these will all be true as final call
11217 
11218  ActionVectorEntry.LocationName = Second;
11219  }
11220  else if(FormatType == PassTime) // new
11221  {
11222  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
11223  {
11224  ;
11225  }
11226  ActionVectorEntry.Command = Second;
11227  ActionVectorEntry.LocationName = Third;
11228  }
11229  else if(FormatType == TimeTimeLoc)
11230  {
11231  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
11232  {
11233  ;
11234  }
11235  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
11236  {
11237  ;
11238  }
11239  ActionVectorEntry.LocationName = Third;
11240  }
11241  else if(FormatType == TimeCmd)
11242  {
11243  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
11244  {
11245  ;
11246  }
11247  ActionVectorEntry.Command = Second;
11248  }
11249  else if(FormatType == ExitRailway)
11250  {
11251  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
11252  {
11253  ;
11254  }
11255  ActionVectorEntry.Command = Second;
11256  ActionVectorEntry.ExitList = ExitList;
11257  }
11258  else if(FormatType == StartNew)
11259  {
11260  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
11261  {
11262  ;
11263  }
11264  ActionVectorEntry.Command = Second;
11265  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11266  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11267  if(Fourth == 'S')
11268  {
11269  ActionVectorEntry.SignallerControl = true;
11270  }
11271  }
11272  else if(FormatType == SNTShuttle)
11273  {
11274  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
11275  {
11276  ;
11277  }
11278  ActionVectorEntry.Command = Second;
11279  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11280  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11281  ActionVectorEntry.OtherHeadCode = Fourth;
11282  }
11283  else if(FormatType == SNSShuttle)
11284  {
11285  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
11286  {
11287  ;
11288  }
11289  ActionVectorEntry.Command = Second;
11290  ActionVectorEntry.OtherHeadCode = Third;
11291  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11292  }
11293  else if(FormatType == TimeCmdHeadCode)
11294  {
11295  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
11296  {
11297  ;
11298  }
11299  ActionVectorEntry.Command = Second;
11300  ActionVectorEntry.OtherHeadCode = Third;
11301  }
11302  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11303  {
11304  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11305  {
11306  ;
11307  }
11308  ActionVectorEntry.Command = Second;
11309  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11310  }
11311  else if(FormatType == FSHNewService)
11312  {
11313  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11314  {
11315  ;
11316  }
11317  ActionVectorEntry.Command = Second;
11318  ActionVectorEntry.OtherHeadCode = Third;
11319  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11320  }
11321  else if(FormatType == FinRemHere)
11322  {
11323  ActionVectorEntry.Command = Second;
11324  }
11325  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11326  }
11327  }
11328  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11329  {
11330  OneEntry = NewRemainder;
11331  First = "";
11332  Second = "";
11333  Third = "";
11334  Fourth = "";
11335  RearStartOrRepeatMins = 0;
11336  FrontStartOrRepeatDigits = 0;
11337  NumberOfRepeats = 0;
11338  if((FinishFlag) && (OneEntry[1] != 'R'))
11339  // already had a finish entry
11340  {
11341  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11342  Utilities->CallLogPop(79);
11343  return(false);
11344  }
11345  if(OneEntry[1] != 'R') // must be finish
11346  {
11347  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11348  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11349  {
11350  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11351  Utilities->CallLogPop(757);
11352  return(false);
11353  }
11354  if(SequenceType != Finish)
11355  {
11356  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11357  Utilities->CallLogPop(785);
11358  return(false);
11359  }
11360  if(FinalCall)
11361  {
11362  // interpret & add to ActionVector
11363  TDateTime TempTime;
11364  TActionVectorEntry ActionVectorEntry;
11365  ActionVectorEntry.FormatType = FormatType;
11366  ActionVectorEntry.LocationType = LocationType;
11367  ActionVectorEntry.SequenceType = SequenceType;
11368  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11369  ActionVectorEntry.Warning = Warning;
11370  if(FormatType == TimeCmd)
11371  {
11372  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11373  {
11374  ;
11375  }
11376  ActionVectorEntry.Command = Second;
11377  }
11378  else if(FormatType == TimeCmdHeadCode)
11379  {
11380  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11381  {
11382  ;
11383  }
11384  ActionVectorEntry.Command = Second;
11385  ActionVectorEntry.OtherHeadCode = Third;
11386  }
11387  else if(FormatType == FNSNonRepeatToShuttle)
11388  {
11389  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11390  {
11391  ;
11392  }
11393  ActionVectorEntry.Command = Second;
11394  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11395  }
11396  else if(FormatType == FSHNewService)
11397  {
11398  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11399  {
11400  ;
11401  }
11402  ActionVectorEntry.Command = Second;
11403  ActionVectorEntry.OtherHeadCode = Third;
11404  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11405  }
11406  else if(FormatType == ExitRailway)
11407  {
11408  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11409  {
11410  ;
11411  }
11412  ActionVectorEntry.Command = Second;
11413  ActionVectorEntry.ExitList = ExitList;
11414  }
11415  else if(FormatType == FinRemHere)
11416  {
11417  ActionVectorEntry.Command = Second;
11418  }
11419  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11420  }
11421  }
11422  else // repeat
11423  {
11424  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11425  {
11426  Utilities->CallLogPop(786);
11427  // error messages given in SplitRepeat
11428  return(false);
11429  }
11430  if(FinalCall)
11431  {
11432  TActionVectorEntry ActionVectorEntry;
11433  ActionVectorEntry.FormatType = Repeat;
11434  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11435  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11436  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11437  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11438  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11439  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11440  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11441  }
11442  }
11443  }
11444  }
11445  if(FinalCall)
11446  {
11447  TrainDataVector.push_back(TempTrainDataEntry);
11448  }
11449  }
11450  Utilities->CallLogPop(80);
11451  return(true);
11452 }
11453 
11454 // ---------------------------------------------------------------------------
11455 
11456 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11457 {
11458  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11459  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11460  {
11461  Utilities->CallLogPop(1890);
11462  return(false);
11463  }
11464  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11465  {
11466  Utilities->CallLogPop(1891);
11467  return(false);
11468  }
11469  Utilities->CallLogPop(1892);
11470  return(true);
11471 }
11472 
11473 // ---------------------------------------------------------------------------
11474 
11475 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
11476 // 1st 5 chars must be HH:MM, anything else will be ignored
11477 {
11478  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
11479  if(TimeStr.Length() < 5)
11480  {
11481  Utilities->CallLogPop(926);
11482  return(false);
11483  }
11484  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
11485  {
11486  Utilities->CallLogPop(927);
11487  return(false);
11488  }
11489  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
11490  {
11491  Utilities->CallLogPop(928);
11492  return(false);
11493  }
11494  if(TimeStr[3] != ':')
11495  {
11496  Utilities->CallLogPop(929);
11497  return(false);
11498  }
11499  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
11500  {
11501  Utilities->CallLogPop(930);
11502  return(false);
11503  }
11504  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
11505  {
11506  Utilities->CallLogPop(931);
11507  return(false);
11508  }
11509  while(TimeStr.Length() > 5)
11510  {
11511  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
11512  }
11513  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
11514  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
11515 
11516  if((WholeHours + FracHour) >= 95.98334)
11517  {
11518  Utilities->CallLogPop(1817);
11519  return(false); // > 95h 59m
11520  }
11521  Time = TDateTime((WholeHours + FracHour) / 24);
11522  Utilities->CallLogPop(932);
11523  return(true);
11524 }
11525 
11526 // ---------------------------------------------------------------------------
11527 
11528 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
11529  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
11530  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
11531 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
11532  Return false for failure.
11533  See description above under ProcessOneTimetableLinefor details of train action entries
11534  NB all types set except LocationType for Sns as may be located or not
11535 */{
11536  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
11537  Warning = false;
11538  TDateTime TempTime;
11539 
11540  if(OneEntry.Length() > 0)
11541  {
11542  if(OneEntry[1] == 'W') // warning
11543  {
11544  Warning = true;
11545  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
11546  // strip it off
11547  }
11548  }
11549  if(OneEntry == "Frh")
11550  {
11551  FormatType = FinRemHere;
11552  SequenceType = Finish;
11553  LocationType = AtLocation;
11554  ShuttleLinkType = NotAShuttleLink;
11555  Second = "Frh";
11556  Utilities->CallLogPop(1016);
11557  return(true);
11558  }
11559  if(OneEntry.Length() < 7)
11560  {
11561  Utilities->CallLogPop(907);
11562  return(false); // 'HH:MM;' + at least a one-letter location name
11563  }
11564  int Pos = OneEntry.Pos(';'); // first segment delimiter
11565 
11566  if(Pos != 6)
11567  {
11568  Utilities->CallLogPop(908);
11569  return(false);
11570  // no delimiter or delimiter not in position 6, has to be a time so fail
11571  }
11572  First = OneEntry.SubString(1, 5); // has to be a time
11573  if(!CheckTimeValidity(16, First, TempTime))
11574  {
11575  Utilities->CallLogPop(909);
11576  return(false);
11577  }
11578  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
11579 
11580  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
11581  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
11582  {
11583  if(Remainder.Length() < 7)
11584  {
11585  Utilities->CallLogPop(910);
11586  return(false); // 'HH:MM;' + at least a one-letter location name
11587  }
11588  Pos = Remainder.Pos(';'); // second segment delimiter
11589  if(Pos == 0)
11590  {
11591  Utilities->CallLogPop(911);
11592  return(false);
11593  // no delimiter, has to be one between departure time & location
11594  }
11595  Second = Remainder.SubString(1, 5); // has to be a time
11596  if(!CheckTimeValidity(15, Second, TempTime))
11597  {
11598  Utilities->CallLogPop(912);
11599  return(false);
11600  }
11601  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11602  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
11603  {
11604  Utilities->CallLogPop(913);
11605  return(false);
11606  }
11607  FormatType = TimeTimeLoc;
11608  SequenceType = Intermediate;
11609  LocationType = AtLocation;
11610  ShuttleLinkType = NotAShuttleLink;
11611  Utilities->CallLogPop(914);
11612  return(true);
11613  }
11614  Pos = Remainder.Pos(';'); // second segment delimiter
11615  if(Pos == 0) // no third segment so second must be a location, or cdt
11616  {
11617  Second = Remainder;
11618  if(Second == "cdt")
11619  {
11620  FormatType = TimeCmd;
11621  ShuttleLinkType = NotAShuttleLink;
11622  LocationType = AtLocation;
11623  SequenceType = Intermediate;
11624  Utilities->CallLogPop(915);
11625  return(true);
11626  }
11627  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11628  {
11629  Utilities->CallLogPop(916);
11630  return(false);
11631  }
11632  else
11633  {
11634  FormatType = TimeLoc;
11635  LocationType = AtLocation;
11636  SequenceType = Intermediate;
11637  ShuttleLinkType = NotAShuttleLink;
11638  Utilities->CallLogPop(917);
11639  return(true);
11640  }
11641  }
11642  // here if second segment is a command, with a third & maybe fourth segments as details
11643  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11644  {
11645  Utilities->CallLogPop(918);
11646  return(false);
11647  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11648  }
11649  Second = Remainder.SubString(1, Pos - 1); // command
11650 
11651  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11652  // details
11653  Pos = Remainder.Pos(';'); // third segment delimiter
11654  if(Pos == 0)
11655  {
11656  Third = Remainder;
11657  }
11658  else
11659  {
11660  Third = Remainder.SubString(1, Pos - 1);
11661  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11662  }
11663  if((Second == "Snt") || (Second == "Snt-sh"))
11664  // third has to be 2 element idents with a space between
11665  {
11666  int SpacePos = Third.Pos(' ');
11667  if(SpacePos == 0)
11668  {
11669  Utilities->CallLogPop(919);
11670  return(false); // no space
11671  }
11672  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11673  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11674  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11675  if(CheckLocationsExistInRailway)
11676  {
11677  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11678  {
11679  Utilities->CallLogPop(920);
11680  return(false);
11681  }
11682  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11683  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11684  }
11685  if(Second == "Snt")
11686  {
11687  FormatType = StartNew;
11688  SequenceType = Start;
11689  LocationType = NoLocation;
11690  // can't be set until know whether located or not - done in SecondPassActions
11691  ShuttleLinkType = NotAShuttleLink;
11692  }
11693  else // Snt-sh
11694  {
11695  FormatType = SNTShuttle;
11696  LocationType = AtLocation;
11697  SequenceType = Start;
11698  ShuttleLinkType = ShuttleLink;
11699  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11700  {
11701  Utilities->CallLogPop(1038);
11702  return(false);
11703  }
11704  }
11705  Utilities->CallLogPop(921);
11706  return(true);
11707  }
11708  if(Second == "Sns-sh") // third & fourth have to be headcodes
11709  {
11710  FormatType = SNSShuttle;
11711  LocationType = AtLocation;
11712  SequenceType = Start;
11713  ShuttleLinkType = ShuttleLink;
11714  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11715  {
11716  Utilities->CallLogPop(1039);
11717  return(false);
11718  }
11719  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11720  {
11721  Utilities->CallLogPop(1040);
11722  return(false);
11723  }
11724  Utilities->CallLogPop(1041);
11725  return(true);
11726  }
11727  if(Second == "F-nshs")
11728  {
11729  FormatType = FNSNonRepeatToShuttle;
11730  LocationType = AtLocation;
11731  SequenceType = Finish;
11732  ShuttleLinkType = ShuttleLink;
11733  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11734  {
11735  Utilities->CallLogPop(1047);
11736  return(false);
11737  }
11738  Utilities->CallLogPop(1048);
11739  return(true);
11740  }
11741  if(Second == "Sns-fsh")
11742  {
11743  FormatType = SNSNonRepeatFromShuttle;
11744  LocationType = AtLocation;
11745  SequenceType = Start;
11746  ShuttleLinkType = ShuttleLink;
11747  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11748  {
11749  Utilities->CallLogPop(1098);
11750  return(false);
11751  }
11752  Utilities->CallLogPop(1099);
11753  return(true);
11754  }
11755  if(Second == "Fns-sh") // third & fourth have to be headcodes
11756  {
11757  FormatType = FSHNewService;
11758  LocationType = AtLocation;
11759  SequenceType = Finish;
11760  ShuttleLinkType = ShuttleLink;
11761  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
11762  {
11763  Utilities->CallLogPop(1050);
11764  return(false);
11765  }
11766  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
11767  {
11768  Utilities->CallLogPop(1051);
11769  return(false);
11770  }
11771  Utilities->CallLogPop(1052);
11772  return(true);
11773  }
11774  // new segment for 'pas'
11775  if(Second == "pas") // third has to be a location
11776  {
11777  FormatType = PassTime;
11778  LocationType = EnRoute;
11779  SequenceType = Intermediate;
11780  ShuttleLinkType = NotAShuttleLink;
11781  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
11782  {
11783  Utilities->CallLogPop(1515);
11784  return(false);
11785  }
11786  Utilities->CallLogPop(1516);
11787  return(true);
11788  }
11789  // new segment for revised 'Fer'
11790  if(Second == "Fer")
11791  // third has to be a set of IDs separated by spaces, and at least 1
11792  {
11793  FormatType = ExitRailway;
11794  LocationType = EnRoute;
11795  SequenceType = Finish;
11796  ShuttleLinkType = NotAShuttleLink;
11797  if(CheckLocationsExistInRailway)
11798  {
11799  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
11800  {
11801  Utilities->CallLogPop(1519);
11802  return(false);
11803  }
11804  }
11805  Utilities->CallLogPop(1520);
11806  return(true);
11807  }
11808  // all remainder must be TimeCmdHeadCode types to be valid
11809  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
11810  (Second != "Frh-sh"))
11811  {
11812  Utilities->CallLogPop(922);
11813  return(false); // all TimeCmdHeadCode types
11814  }
11815  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
11816  {
11817  Utilities->CallLogPop(923);
11818  return(false);
11819  }
11820  FormatType = TimeCmdHeadCode;
11821  LocationType = AtLocation;
11822  if(Second == "Frh-sh")
11823  {
11824  ShuttleLinkType = ShuttleLink;
11825  }
11826  else
11827  {
11828  ShuttleLinkType = NotAShuttleLink;
11829  }
11830  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
11831  {
11832  SequenceType = Finish;
11833  }
11834  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
11835  {
11836  SequenceType = Intermediate;
11837  }
11838  if((Second == "Sfs") || (Second == "Sns"))
11839  {
11840  SequenceType = Start;
11841  }
11842  Utilities->CallLogPop(924);
11843  return(true);
11844 }
11845 
11846 // ---------------------------------------------------------------------------
11847 
11848 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
11849 {
11850  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
11851  // and contains no special characters
11852  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
11853  if(LocStr == "")
11854  {
11855  Utilities->CallLogPop(1353);
11856  return(false); // has to have at least one character
11857  }
11858  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
11859  {
11860  Utilities->CallLogPop(1354);
11861  return(false); // can't begin with a number
11862  }
11863  for(int x = 1; x < LocStr.Length() + 1; x++)
11864  {
11865  if(LocStr[x] < ' ')
11866  {
11867  Utilities->CallLogPop(1355);
11868  return(false); // contains a special character
11869  }
11870  if(LocStr[x] > 'z')
11871  {
11872  Utilities->CallLogPop(1356);
11873  return(false); // contains a character outside the standard ASCII set
11874  }
11875  }
11876  // check exists in railway location list if CheckLocationsExistInRailway is true
11877  if(CheckLocationsExistInRailway)
11878  {
11879  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
11880  {
11881  TimetableMessage(GiveMessages, "Location name '" + LocStr +
11882  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
11883  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
11884  "that includes a continuation will not be valid.");
11885  Utilities->CallLogPop(1357);
11886  return(false);
11887  }
11888  }
11889  Utilities->CallLogPop(1358);
11890  return(true);
11891 }
11892 
11893 // ---------------------------------------------------------------------------
11894 
11895 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11896 {
11897  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11898  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11899  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11900  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11901  HeadCode);
11902  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11903  {
11904  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11905  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11906  Utilities->CallLogPop(1359);
11907  return(false);
11908  }
11909  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11910  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11911  {
11912  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11913  {
11914  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11915  Utilities->CallLogPop(1895);
11916  return(false);
11917  }
11918  }
11919  // secondly ensure the true Headcode only has letters or digits
11920  for(int x = 3; x >= 0; x--)
11921  {
11922  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11923  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11924  {
11925  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11926  Utilities->CallLogPop(1790);
11927  return(false);
11928  }
11929  }
11930  Utilities->CallLogPop(1364);
11931  return(true);
11932 }
11933 
11934 // ---------------------------------------------------------------------------
11935 
11936 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
11937 // set of track element IDs, separated by spaces, and at least 1 present
11938 {
11939  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
11940  ExitList.clear();
11941  AnsiString CurrentID = "";
11942 
11943  if(IDSet.Length() == 0)
11944  {
11945  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11946  Utilities->CallLogPop(1521);
11947  return(false);
11948  }
11949  for(int x = 1; x <= IDSet.Length(); x++)
11950  {
11951  char C = IDSet[x];
11952  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11953  {
11954  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11955  Utilities->CallLogPop(1522);
11956  return(false);
11957  }
11958 /* don't use, error checks in GetTrackVectorPositionFromString instead
11959  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
11960  {
11961  if((x==1) || (x == IDSet.Length()))
11962  {
11963  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
11964  Utilities->CallLogPop(2479);
11965  return(false);
11966  }
11967  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
11968  {
11969  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
11970  Utilities->CallLogPop(2480);
11971  return(false);
11972  }
11973  }
11974 */
11975  }
11976  int Pos = IDSet.Pos(' '); // look for the first space
11977 
11978  while(true)
11979  {
11980  if(Pos == 0)
11981  {
11982  CurrentID = IDSet;
11983  IDSet = "";
11984  }
11985  else
11986  {
11987  CurrentID = IDSet.SubString(1, Pos - 1);
11988  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11989  }
11990  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11991  if(VecPos == -1)
11992  {
11993  Utilities->CallLogPop(1523);
11994  return(false); // messages given in GetTrackVectorPositionFromString
11995  }
11996  else
11997  {
11998  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11999  {
12000  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
12001  Utilities->CallLogPop(1524);
12002  return(false);
12003  }
12004  else
12005  {
12006  // first check for duplicates
12007  if(!ExitList.empty())
12008  {
12009  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
12010  {
12011  if(*ELIT == VecPos)
12012  {
12013  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
12014  Utilities->CallLogPop(1532);
12015  return(false);
12016  }
12017  }
12018  }
12019  // of OK add it to the list
12020  ExitList.push_back(VecPos);
12021  }
12022  }
12023  if(IDSet == "")
12024  {
12025  Utilities->CallLogPop(1525);
12026  return(true);
12027  }
12028  else
12029  {
12030  Pos = IDSet.Pos(' '); // look for the next space
12031  }
12032  } // while(true)
12033 }
12034 
12035 // ---------------------------------------------------------------------------
12036 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
12037  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
12038 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
12039 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
12040 // of each item
12041 {
12042  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
12043  int Pos = 0;
12044  AnsiString Remainder = "";
12045  int SemiColonCount = 0;
12046 
12047  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
12048  {
12049  if(TrainInfoStr[x] == ';')
12050  {
12051  SemiColonCount++;
12052  }
12053  }
12054  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
12055  {
12056  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
12057  "'. Should be headcode + optional description for a continuing service;" +
12058  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
12059  Utilities->CallLogPop(880);
12060  return(false);
12061  }
12062  if(SemiColonCount == 0)
12063  {
12064  HeadCode = TrainInfoStr;
12065  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
12066  {
12067  Utilities->CallLogPop(881);
12068  return(false);
12069  }
12070  Utilities->CallLogPop(882);
12071  return(true);
12072  }
12073  if(SemiColonCount == 1) // headcode & description only
12074  {
12075  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12076  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12077  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12078  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
12079  {
12080  Utilities->CallLogPop(883);
12081  return(false);
12082  }
12083  if(Description == "")
12084  {
12085  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12086  Utilities->CallLogPop(884);
12087  return(false);
12088  }
12089  if(Description.Length() > 60)
12090  {
12091  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12092  Utilities->CallLogPop(1157);
12093  return(false);
12094  }
12095  for(int x = 1; x < Description.Length() + 1; x++)
12096  {
12097  if((Description[x] < ' ') || (Description[x] > '~'))
12098  {
12099  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12100  Utilities->CallLogPop(885);
12101  return(false);
12102  }
12103  }
12104  Utilities->CallLogPop(886);
12105  return(true);
12106  }
12107  // if here must have 6 or 7 semicolons
12108  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12109  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12110  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12111  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
12112  {
12113  Utilities->CallLogPop(887);
12114  return(false);
12115  }
12116  Pos = Remainder.Pos(';'); // 2nd delimiter
12117  Description = Remainder.SubString(1, Pos - 1);
12118  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12119  if(Description == "")
12120  {
12121  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12122  Utilities->CallLogPop(888);
12123  return(false);
12124  }
12125  if(Description.Length() > 60)
12126  {
12127  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12128  Utilities->CallLogPop(1158);
12129  return(false);
12130  }
12131  for(int x = 1; x < Description.Length() + 1; x++)
12132  {
12133  if((Description[x] < ' ') || (Description[x] > 126))
12134  {
12135  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12136  Utilities->CallLogPop(889);
12137  return(false);
12138  }
12139  }
12140  Pos = Remainder.Pos(';'); // 3rd delimiter
12141  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
12142 
12143  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12144  if(StartSpeedStr == "")
12145  {
12146  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
12147  Utilities->CallLogPop(890);
12148  return(false);
12149  }
12150  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
12151  {
12152  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
12153  {
12154  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
12155  Utilities->CallLogPop(891);
12156  return(false);
12157  }
12158  }
12159  StartSpeed = StartSpeedStr.ToInt();
12160  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12161  {
12162  StartSpeed = TTrain::MaximumSpeedLimit;
12163  if(!SSHigh) // added at v2.4.0
12164  {
12165  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12166  SSHigh = true;
12167  }
12168  }
12169  Pos = Remainder.Pos(';'); // 4th delimiter
12170  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
12171 
12172  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12173  if(MaxRunningSpeedStr == "")
12174  {
12175  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
12176  Utilities->CallLogPop(892);
12177  return(false);
12178  }
12179  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
12180  {
12181  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
12182  {
12183  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
12184  Utilities->CallLogPop(893);
12185  return(false);
12186  }
12187  }
12188  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
12189  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12190  {
12191  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
12192  if(!MRSHigh) // added at v2.4.0
12193  {
12194  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12195  MRSHigh = true;
12196  }
12197  }
12198  if(MaxRunningSpeed < 10)
12199  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12200  {
12201  MaxRunningSpeed = 10;
12202  if(!MRSLow) // added at v2.4.0
12203  {
12204  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12205  MRSLow = true;
12206  }
12207  }
12208  Pos = Remainder.Pos(';'); // 5th delimiter
12209  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
12210 
12211  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12212  if(MassStr == "")
12213  {
12214  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
12215  Utilities->CallLogPop(895);
12216  return(false);
12217  }
12218  for(int x = 1; x < MassStr.Length() + 1; x++)
12219  {
12220  if((MassStr[x] < '0') || (MassStr[x] > '9'))
12221  {
12222  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
12223  Utilities->CallLogPop(896);
12224  return(false);
12225  }
12226  }
12227  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
12228  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
12229  {
12230  Mass = TTrain::MaximumMassLimit;
12231  if(!MassHigh) // added at v2.4.0
12232  {
12233  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
12234  MassHigh = true;
12235  }
12236  }
12237  if(Mass == 0)
12238  {
12239  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
12240  Utilities->CallLogPop(897);
12241  return(false);
12242  }
12243  Pos = Remainder.Pos(';'); // 6th delimiter
12244  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
12245 
12246  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12247  if(MaxBrakeForceStr == "")
12248  {
12249  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
12250  Utilities->CallLogPop(898);
12251  return(false);
12252  }
12253  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
12254  {
12255  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
12256  {
12257  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
12258  Utilities->CallLogPop(899);
12259  return(false);
12260  }
12261  }
12262  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
12263 
12264  // convert to kg force
12265  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
12266  {
12267  MaxBrakeForce = Mass;
12268  if(!BFHigh) // added at v2.4.0
12269  {
12270  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
12271  BFHigh = true;
12272  }
12273  }
12274  if((MaxBrakeForce / Mass) < 0.01)
12275  {
12276  MaxBrakeForce = Mass * 0.01;
12277  if(!BFLow) // added at v2.4.0
12278  {
12279  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
12280  BFLow = true;
12281  }
12282  }
12283  // convert to m/s/s
12284  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
12285  // now may have just a power entry or power and signaller max. speed
12286  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
12287 
12288  if(SemiColonCount == 6)
12289  {
12290  GrossPowerStr = Remainder;
12291  SignallerSpeedStr = "30"; // default value
12292  }
12293  else // must be 7
12294  {
12295  Pos = Remainder.Pos(';'); // 7th delimiter
12296  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12297  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12298  }
12299  // deal with GrossPower
12300  if(GrossPowerStr == "")
12301  {
12302  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12303  Utilities->CallLogPop(901);
12304  return(false);
12305  }
12306  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12307  {
12308  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12309  {
12310  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12311  Utilities->CallLogPop(902);
12312  return(false);
12313  }
12314  }
12315 
12316  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12317 
12318  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12319  {
12320  GrossPower = TTrain::MaximumPowerLimit;
12321  if(!PwrHigh)
12322  {
12323  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12324  PwrHigh = true;
12325  }
12326  }
12327  else if(GrossPower == 0) // changed at v2.4.0
12328  {
12329  GrossPower = 0.1;
12330  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12331  }
12332  else if((GrossPower > 0) && (GrossPower < 10000))
12333  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12334  {
12335  GrossPower = 10000;
12336  }
12337  PowerAtRail = GrossPower * 0.8;
12338  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12339 
12340  // deal with SignallerSpeed
12341  if(SignallerSpeedStr == "")
12342  {
12343  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12344  Utilities->CallLogPop(1771);
12345  return(false);
12346  }
12347  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12348  {
12349  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12350  {
12351  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12352  Utilities->CallLogPop(1769);
12353  return(false);
12354  }
12355  }
12356  SignallerSpeed = SignallerSpeedStr.ToInt();
12357  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12358  {
12359  SignallerSpeed = TTrain::MaximumSpeedLimit;
12360  if(!SigSHigh)
12361  {
12362  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12363  SigSHigh = true;
12364  }
12365  }
12366  if(SignallerSpeed < 10)
12367  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12368  {
12369  SignallerSpeed = 10;
12370  if(!SigSLow)
12371  {
12372  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12373  SigSLow = true;
12374  }
12375  // Utilities->CallLogPop(1770);
12376  // return false;
12377  }
12378  Utilities->CallLogPop(904);
12379  return(true);
12380 }
12381 
12382 // ---------------------------------------------------------------------------
12383 
12384 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
12385  bool GiveMessages)
12386 {
12387  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
12388  // function checks validity of each item and returns false for error
12389  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
12390  if(OneEntry.Length() < 7)
12391  {
12392  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12393  Utilities->CallLogPop(865);
12394  return(false);
12395  }
12396  int SemiColonCount = 0;
12397 
12398  for(int x = 1; x < OneEntry.Length() + 1; x++)
12399  {
12400  if(OneEntry[x] == ';')
12401  {
12402  SemiColonCount++;
12403  }
12404  }
12405  if(SemiColonCount != 3)
12406  {
12407  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12408  Utilities->CallLogPop(866);
12409  return(false);
12410  }
12411  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
12412  {
12413  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12414  Utilities->CallLogPop(867);
12415  return(false);
12416  }
12417  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
12418  // strip off R;
12419 
12420  int Pos = 0;
12421 
12422  Pos = Remainder.Pos(';');
12423  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
12424 
12425  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12426  if(MinutesStr == "")
12427  {
12428  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
12429  Utilities->CallLogPop(868);
12430  return(false);
12431  }
12432  if(MinutesStr.Length() > 3)
12433  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
12434  {
12435  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
12436  Utilities->CallLogPop(2119);
12437  return(false);
12438  }
12439  for(int x = 1; x < MinutesStr.Length() + 1; x++)
12440  {
12441  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
12442  {
12443  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
12444  Utilities->CallLogPop(869);
12445  return(false);
12446  }
12447  }
12448  RearStartOrRepeatMins = MinutesStr.ToInt();
12449  if(RearStartOrRepeatMins == 0)
12450  {
12451  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
12452  Utilities->CallLogPop(870);
12453  return(false);
12454  }
12455  Pos = Remainder.Pos(';');
12456  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
12457 
12458  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12459  if(DigitsStr == "")
12460  {
12461  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
12462  Utilities->CallLogPop(871);
12463  return(false);
12464  }
12465  for(int x = 1; x < DigitsStr.Length() + 1; x++)
12466  {
12467  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
12468  {
12469  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
12470  Utilities->CallLogPop(872);
12471  return(false);
12472  }
12473  }
12474  if(DigitsStr.Length() > 2)
12475  {
12476  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
12477  Utilities->CallLogPop(873);
12478  return(false);
12479  }
12480  FrontStartOrRepeatDigits = DigitsStr.ToInt();
12481 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
12482  route rather than the service
12483  if(FrontStartOrRepeatDigits == 0)
12484  {
12485  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
12486  Utilities->CallLogPop(874);
12487  return false;
12488  }
12489 */
12490  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
12491  // new for v0.6b for unrestricted headcodes
12492  {
12493  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
12494  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
12495  Utilities->CallLogPop(1889);
12496  return(false);
12497  }
12498  AnsiString NumberStr = Remainder;
12499 
12500  if(NumberStr == "")
12501  {
12502  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
12503  Utilities->CallLogPop(875);
12504  return(false);
12505  }
12506  if(NumberStr.Length() > 4)
12507  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
12508  {
12509  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
12510  Utilities->CallLogPop(2118);
12511  return(false);
12512  }
12513  for(int x = 1; x < NumberStr.Length() + 1; x++)
12514  {
12515  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
12516  // catches negative numbers
12517  {
12518  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
12519  Utilities->CallLogPop(876);
12520  return(false);
12521  }
12522  }
12523  NumberOfRepeats = NumberStr.ToInt();
12524  if(NumberOfRepeats == 0)
12525  {
12526  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
12527  Utilities->CallLogPop(877);
12528  return(false);
12529  }
12530  Utilities->CallLogPop(878);
12531  return(true);
12532 }
12533 
12534 // ---------------------------------------------------------------------------
12535 
12536 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
12537 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
12538  vector rather than the timetable
12539  Note also that for unlocated Snt entries the LocationType hasn't yet been set
12540 
12541  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
12542 
12543  For info:-
12544  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
12545  {
12546  public:
12547  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
12549  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
12550  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
12551  int NumberOfRepeats; ///< the number of repeating services
12552  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
12554  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
12556  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
12557  TTimetableFormatType FormatType; ///< defines the timetable action type
12558  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
12559  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
12560  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
12561  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
12563  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
12565 
12566  // inline function
12567 
12569  TActionVectorEntry() {
12570  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
12571  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
12572  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
12573  Warning = false; SignallerControl = false;
12574  }
12575  };
12576 
12577  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
12578 
12579  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
12580  {
12581  public:
12582  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
12585  double MaxBrakeRate; ///< in metres/sec/sec
12586  double MaxRunningSpeed; ///< in km/h
12587  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
12588  int Mass; ///< in kg
12589  int NumberOfTrains; ///< number of repeats + 1
12590  int SignallerSpeed; ///< in km/h for use when under signaller control
12591  int StartSpeed; ///< in km/h
12592  TActionVector ActionVector; ///< all the actions for the train
12593  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
12594 
12595  //inline function
12596 
12598  TTrainDataEntry()
12599  {
12600  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
12601  }
12602  };
12603 
12604  Allowable successors:-
12605  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
12606  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12607  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12608  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12609  set location, else fails)
12610  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12611  set location, else fails)
12612  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12613  set location, else fails)
12614  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12615  set location, else fails)
12616  Fns -> R only
12617  F-nshs -> Nothing (no repeats permitted)
12618  Fjo -> R only
12619  Frh -> R only
12620  Fer -> R only
12621  Frh-sh -> R only
12622  Fns-sh -> R only
12623  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
12624  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12625  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12626  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12627  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12628  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12629  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12630  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12631  Repeat -> Nothing
12632 
12633  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
12634  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
12635  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
12636  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
12637  Check all times increase or stay same through ActionVector
12638  Cycle through all entries in vector setting arr & dep times based on above list
12639  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
12640  Check locations match the arr & dep TimeLoc entries
12641  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
12642  Make above valid succession checks
12643  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
12644  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
12645  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12646  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
12647  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
12648  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12649  Set train info for Sfs & Sns entries
12650  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
12651  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
12652  element at each end, or length of 3 & 1 extra element at either end
12653  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
12654  Check all Cmds have EventTime set & Arr & Dep times = -1
12655  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
12656  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
12657 
12658  Give messages in function if errors detected and clear the vector. Return false for failure.
12659 */
12660 
12661 /* Earlier checks:-
12662  Checks carried out with error messages in this function:-
12663  At least one comma in the line (it's based on a csv file);
12664  No entries following train information;
12665  At least one comma in remainder after train information (i.e at least a start and a finish entry);
12666  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
12667  First entry not a start entry;
12668  Train information incomplete before a start entry;
12669  Entry follows a finish entry but doesn't begin with 'R';
12670  SplitEntry returns false in a finish entry - message repeats the entry for information;
12671  Last action entry isn't a finish entry.
12672 
12673  Function returns false with no message if:-
12674  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12675  time is found at all then an error message is given in the calling function);
12676  SplitTrainInfo returns false (message given in called function);
12677  SplitRepeat returns false (message given in called function).
12678 
12679 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
12680 
12681 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
12682  Code ShuttleLink- EntryPtr ShuttleLink
12683  HeadCode EntryPtr
12684 
12685 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12686 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12687 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12688 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
12689 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12690 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12691 
12692 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12693 
12694 */{
12695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12696  if(TrainDataVector.empty())
12697  {
12698  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
12699  TrainDataVector.clear();
12700  Utilities->CallLogPop(1832);
12701  return(false);
12702  }
12703 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
12704  1) must have at least one actionvector entry
12705  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12706  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12707  4) first entry must be a start;
12708  4a) if first entry is Snt and not signallercontrol and second is a finish then it must be at a location with zero start speed
12709  5) a start must be the first entry;
12710  6) a repeat entry must be the last;
12711  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12712  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12713  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12714 */
12715 
12716  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
12717  TwoLocationFlag = false; //added at v2.9.1
12718  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
12719  {
12720  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12721  if(TrainDataVector.at(x).ActionVector.empty())
12722  {
12723  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
12724  TrainDataVector.clear();
12725  Utilities->CallLogPop(1833);
12726  return(false);
12727  }
12728  }
12729  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
12730  {
12731  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12732  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12733  if(!(AVEntry0.SignallerControl))
12734  {
12735  if(TrainDataVector.at(x).ActionVector.size() == 1)
12736  {
12737  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
12738  TrainDataVector.clear();
12739  Utilities->CallLogPop(1822);
12740  return(false);
12741  }
12742  }
12743  }
12744  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
12745  {
12746  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12747  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12748  if(AVEntry0.SignallerControl)
12749  {
12750  if(TrainDataVector.at(x).ActionVector.size() > 2)
12751  {
12752  SecondPassMessage(GiveMessages,
12753  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
12754  TDEntry.HeadCode);
12755  TrainDataVector.clear();
12756  Utilities->CallLogPop(1837);
12757  return(false);
12758  }
12759  if(TrainDataVector.at(x).ActionVector.size() > 1)
12760  {
12761  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12762  if(AVEntry1.FormatType != Repeat)
12763  {
12764  SecondPassMessage(GiveMessages,
12765  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
12766  TrainDataVector.clear();
12767  Utilities->CallLogPop(1838);
12768  return(false);
12769  }
12770  }
12771  }
12772  }
12773  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
12774  {
12775  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12776  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12777  if(AVEntry0.SequenceType != Start)
12778  {
12779  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
12780  TrainDataVector.clear();
12781  Utilities->CallLogPop(1824);
12782  return(false);
12783  }
12784  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
12785  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
12786  // and others for a located Snt, but those checks done later
12787  {
12788  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12789  // must be a second entry if first not signallercontrol
12790  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
12791  {
12792  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
12793  TDEntry.HeadCode);
12794  TrainDataVector.clear();
12795  Utilities->CallLogPop(2046);
12796  return(false);
12797  }
12798  }
12799  }
12800  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
12801  {
12802  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12803  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12804  {
12805  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12806  if((AVEntry.SequenceType == Start) && (y != 0))
12807  {
12808  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
12809  TrainDataVector.clear();
12810  Utilities->CallLogPop(1825);
12811  return(false);
12812  }
12813  }
12814  }
12815  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
12816  {
12817  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12818  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12819  {
12820  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12821  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
12822  {
12823  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
12824  TrainDataVector.clear();
12825  Utilities->CallLogPop(1826);
12826  return(false);
12827  }
12828  }
12829  }
12830  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
12831  {
12832  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12833  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12834  {
12835  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12836  if((y == 0) && AVEntry.SignallerControl)
12837  {
12838  break;
12839  }
12840  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
12841  {
12842  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
12843  {
12844  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
12845  TrainDataVector.clear();
12846  Utilities->CallLogPop(1827);
12847  return(false);
12848  }
12849  if(AVEntry.FormatType == Repeat)
12850  {
12851  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
12852  if(LastAVEntry.SequenceType != Finish)
12853  {
12854  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
12855  TrainDataVector.clear();
12856  Utilities->CallLogPop(1828);
12857  return(false);
12858  }
12859  }
12860  }
12861  }
12862  }
12863  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
12864  {
12865  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12866  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12867  {
12868  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12869  if(AVEntry.SequenceType == Finish)
12870  {
12871  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
12872  {
12873  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
12874  TrainDataVector.clear();
12875  Utilities->CallLogPop(1829);
12876  return(false);
12877  }
12878  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
12879  {
12880  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12881  {
12882  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
12883  TDEntry.HeadCode);
12884  TrainDataVector.clear();
12885  Utilities->CallLogPop(1830);
12886  return(false);
12887  }
12888  }
12889  }
12890  }
12891  }
12892 
12893  // end of new preliminary checks
12894 
12895  // check ActionVector present and check start event successor validity
12896  // For Snt & Snt-sh set location if stopped, don't set any times yet
12897  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12898  {
12899  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12900  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12901  // use reference so can change internals where necessary
12902  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
12903  {
12904  AnsiString LocationName = "";
12905  if(IsSNTEntryLocated(0, TDEntry, LocationName))
12906  // it is at a location
12907  {
12908  if(TDEntry.StartSpeed == 0) // stopped
12909  {
12910  AVEntry0.LocationName = LocationName;
12911  AVEntry0.LocationType = AtLocation;
12912  // check successor validity for located Snt that isn't a SignallerControl entry
12913  if(!AVEntry0.SignallerControl)
12914  {
12915  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12916  // at least 2 entries present checked in integrity check so (1) valid
12917  if(!AtLocSuccessor(AVEntry1))
12918  {
12919  // Frh following Snt-sh will return false in location check, so no need to check here
12920  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12921  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12922  TrainDataVector.clear();
12923  Utilities->CallLogPop(523);
12924  return(false);
12925  }
12926  }
12927  }
12928  else
12929  {
12930  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12931  TDEntry.HeadCode);
12932  TrainDataVector.clear();
12933  Utilities->CallLogPop(791);
12934  return(false);
12935  }
12936  }
12937  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12938  {
12939  if(AVEntry0.Command == "Snt-sh")
12940  {
12941  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12942  TrainDataVector.clear();
12943  Utilities->CallLogPop(1042);
12944  return(false);
12945  }
12946  AVEntry0.LocationType = EnRoute;
12947  if(!AVEntry0.SignallerControl)
12948  {
12949  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12950  // at least 2 entries checked in integrity check so (1) valid
12951  if(!MovingSuccessor(AVEntry1))
12952  {
12953  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12954  TDEntry.HeadCode);
12955  TrainDataVector.clear();
12956  Utilities->CallLogPop(790);
12957  return(false);
12958  }
12959  }
12960  }
12961  }
12962  // check other start successors
12963  else if(AVEntry0.SequenceType == Start)
12964  {
12965  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12966  // at least 2 entries present checked in integrity check so (1) valid
12967  if(!AtLocSuccessor(AVEntry1))
12968  {
12969  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12970  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12971  TrainDataVector.clear();
12972  Utilities->CallLogPop(793);
12973  return(false);
12974  }
12975  }
12976  }
12977 
12978  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12979  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12980  {
12981  bool FoundFlag = false;
12982  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12983  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12984  // use reference so can change internals
12985  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12986  {
12987  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12988  {
12989  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12990  if(AVEntry2.FormatType == TimeLoc)
12991  {
12992  FoundFlag = true;
12993  AVEntry.LocationName = AVEntry2.LocationName;
12994  break;
12995  }
12996  }
12997  if(!FoundFlag)
12998  {
12999  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
13000  TDEntry.HeadCode);
13001  TrainDataVector.clear();
13002  Utilities->CallLogPop(851);
13003  return(false);
13004  }
13005  }
13006  }
13007 
13008  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
13009  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13010  {
13011  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13012  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13013  {
13014  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13015  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
13016  {
13017  if(AVEntry.LocationName == "")
13018  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
13019  {
13020  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
13021  ": an event should have had a location name associated with it but it could not be found");
13022  TrainDataVector.clear();
13023  Utilities->CallLogPop(1831);
13024  return(false);
13025  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
13026  }
13027  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13028  {
13029  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13030  // use reference so can change internals where necessary
13031  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
13032  {
13033  AVEntry2.LocationName = AVEntry.LocationName;
13034  }
13035  else
13036  {
13037  break;
13038  }
13039  }
13040  }
13041  }
13042  }
13043  // all location names now set
13044 
13045  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
13046  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13047  {
13048  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13049  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13050  {
13051  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13052  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
13053  {
13054  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
13055  // i.e at least one more, must be a repeat
13056  {
13057  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13058  {
13059  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
13060  TrainDataVector.clear();
13061  Utilities->CallLogPop(798);
13062  return(false);
13063  }
13064  }
13065  }
13066  if(AVEntry.Command == "F-nshs")
13067  {
13068  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13069  // i.e has to be the last
13070  {
13071  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
13072  TrainDataVector.clear();
13073  Utilities->CallLogPop(1049);
13074  return(false);
13075  }
13076  }
13077  if(AVEntry.Command == "pas")
13078  {
13079  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13080  {
13081  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
13082  TrainDataVector.clear();
13083  Utilities->CallLogPop(1518);
13084  return(false);
13085  }
13086  }
13087  if(AVEntry.Command == "jbo")
13088  {
13089  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13090  {
13091  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
13092  TrainDataVector.clear();
13093  Utilities->CallLogPop(800);
13094  return(false);
13095  }
13096  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13097  if(!AtLocSuccessor(AVEntry2))
13098  {
13099  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
13100  ". The event isn't valid for a stationary train.");
13101  TrainDataVector.clear();
13102  Utilities->CallLogPop(801);
13103  return(false);
13104  }
13105  }
13106  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13107  {
13108  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13109  {
13110  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
13111  TrainDataVector.clear();
13112  Utilities->CallLogPop(802);
13113  return(false);
13114  }
13115  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13116  if(!AtLocSuccessor(AVEntry2))
13117  {
13118  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
13119  ". The event isn't valid for a stationary train.");
13120  TrainDataVector.clear();
13121  Utilities->CallLogPop(803);
13122  return(false);
13123  }
13124  }
13125  if(AVEntry.Command == "cdt")
13126  {
13127  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13128  {
13129  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
13130  TrainDataVector.clear();
13131  Utilities->CallLogPop(804);
13132  return(false);
13133  }
13134  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13135  if(!AtLocSuccessor(AVEntry2))
13136  {
13137  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
13138  ". The event isn't valid for a stationary train.");
13139  TrainDataVector.clear();
13140  Utilities->CallLogPop(805);
13141  return(false);
13142  }
13143  }
13144  if(AVEntry.FormatType == TimeTimeLoc)
13145  {
13146  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13147  {
13148  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
13149  TrainDataVector.clear();
13150  Utilities->CallLogPop(806);
13151  return(false);
13152  }
13153  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13154  if(!MovingSuccessor(AVEntry2))
13155  {
13156  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
13157  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
13158  TrainDataVector.clear();
13159  Utilities->CallLogPop(807);
13160  return(false);
13161  }
13162  }
13163  if(AVEntry.FormatType == PassTime)
13164  {
13165  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13166  {
13167  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
13168  TrainDataVector.clear();
13169  Utilities->CallLogPop(1530);
13170  return(false);
13171  }
13172  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13173  if(!MovingSuccessor(AVEntry2))
13174  {
13175  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
13176  ". The event isn't valid for a moving train.");
13177  TrainDataVector.clear();
13178  Utilities->CallLogPop(1531);
13179  return(false);
13180  }
13181  }
13182  if(AVEntry.FormatType == Repeat)
13183  {
13184  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13185  {
13186  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
13187  TrainDataVector.clear();
13188  Utilities->CallLogPop(808);
13189  return(false);
13190  }
13191  }
13192  }
13193  }
13194 
13195  // set arrival & departure times for TimeLocs & set their EventTimes to -1
13196  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13197  {
13198  bool LastEntryIsAnArrival = false;
13199  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13200  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
13201  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13202  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13203  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
13204  {
13205  LastEntryIsAnArrival = false;
13206  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13207  {
13208  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13209  if(AVEntry.FormatType == TimeLoc)
13210  {
13211  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
13212  {
13213  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
13214  }
13215  if(LastEntryIsAnArrival)
13216  {
13217  AVEntry.DepartureTime = AVEntry.EventTime;
13218  AVEntry.EventTime = TDateTime(-1);
13219  LastEntryIsAnArrival = false;
13220  }
13221  else // last entry a departure
13222  {
13223  AVEntry.ArrivalTime = AVEntry.EventTime;
13224  AVEntry.EventTime = TDateTime(-1);
13225  LastEntryIsAnArrival = true;
13226  }
13227  }
13228  }
13229  }
13230  else // all others stopped at beginning
13231  {
13232  LastEntryIsAnArrival = true;
13233  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13234  {
13235  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13236  if(AVEntry.FormatType == TimeLoc)
13237  {
13238  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
13239  {
13240  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
13241  }
13242  if(LastEntryIsAnArrival)
13243  {
13244  AVEntry.DepartureTime = AVEntry.EventTime;
13245  AVEntry.EventTime = TDateTime(-1);
13246  LastEntryIsAnArrival = false;
13247  }
13248  else // last entry a departure
13249  {
13250  AVEntry.ArrivalTime = AVEntry.EventTime;
13251  AVEntry.EventTime = TDateTime(-1);
13252  LastEntryIsAnArrival = true;
13253  }
13254  }
13255  }
13256  }
13257  }
13258  // perform remaining successor checks for TimeLocs
13259  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13260  {
13261  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13262  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13263  {
13264  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13265  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
13266  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
13267  {
13268  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13269  {
13270  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
13271  TrainDataVector.clear();
13272  Utilities->CallLogPop(809);
13273  return(false);
13274  }
13275  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13276  if(!AtLocSuccessor(AVEntry2))
13277  {
13278  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
13279  ". The event isn't valid for a stationary train.");
13280  TrainDataVector.clear();
13281  Utilities->CallLogPop(810);
13282  return(false);
13283  }
13284  }
13285  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
13286  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
13287  {
13288  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13289  {
13290  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
13291  TrainDataVector.clear();
13292  Utilities->CallLogPop(811);
13293  return(false);
13294  }
13295  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13296  if(!MovingSuccessor(AVEntry2))
13297  {
13298  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
13299  ". The event isn't valid for a moving train.");
13300  TrainDataVector.clear();
13301  Utilities->CallLogPop(812);
13302  return(false);
13303  }
13304  }
13305  }
13306  }
13307 
13308  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
13309  // & repeats have no times set
13310  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13311  {
13312  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13313  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13314  {
13315  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13316  if(AVEntry.FormatType == TimeLoc)
13317  {
13318  if(AVEntry.EventTime != TDateTime(-1))
13319  {
13320  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
13321  }
13322  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
13323  {
13324  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
13325  }
13326  }
13327  if(AVEntry.FormatType == TimeTimeLoc)
13328  {
13329  if(AVEntry.EventTime != TDateTime(-1))
13330  {
13331  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
13332  }
13333  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
13334  {
13335  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
13336  }
13337  }
13338  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
13339  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
13340  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
13341  {
13342  if(AVEntry.EventTime == TDateTime(-1))
13343  {
13344  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
13345  }
13346  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
13347  {
13348  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
13349  }
13350  }
13351  if(AVEntry.FormatType == Repeat)
13352  {
13353  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
13354  {
13355  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
13356  }
13357  }
13358  }
13359  }
13360 
13361  // check times stay same or increase, note that can have time of 0 if include midnight
13362  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13363  {
13364  TDateTime CurrentTime = TTClockTime; // the timetable start time
13365  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13366  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13367  {
13368  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13369  if(AVEntry.FormatType == Repeat)
13370  {
13371  break;
13372  }
13373  if(AVEntry.FormatType == FinRemHere)
13374  {
13375  break;
13376  }
13377  if(AVEntry.FormatType == TimeTimeLoc)
13378  {
13379  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
13380  {
13381  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
13382  TDEntry.HeadCode);
13383  TrainDataVector.clear();
13384  Utilities->CallLogPop(813);
13385  return(false);
13386  }
13387  if(AVEntry.ArrivalTime < CurrentTime)
13388  {
13389  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
13390  TDEntry.HeadCode);
13391  TrainDataVector.clear();
13392  Utilities->CallLogPop(814);
13393  return(false);
13394  }
13395  CurrentTime = AVEntry.DepartureTime;
13396  continue;
13397  }
13398  if(AVEntry.FormatType == TimeLoc)
13399  {
13400  if(AVEntry.ArrivalTime >= TDateTime(0))
13401  {
13402  if(AVEntry.ArrivalTime < CurrentTime)
13403  {
13404  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
13405  TrainDataVector.clear();
13406  Utilities->CallLogPop(815);
13407  return(false);
13408  }
13409  CurrentTime = AVEntry.ArrivalTime;
13410  }
13411  else
13412  {
13413  if(AVEntry.DepartureTime < CurrentTime)
13414  // both may be 0 legitimately so must allow for this
13415  {
13416  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
13417  TrainDataVector.clear();
13418  Utilities->CallLogPop(816);
13419  return(false);
13420  }
13421  CurrentTime = AVEntry.DepartureTime;
13422  }
13423  continue;
13424  }
13425  if(AVEntry.EventTime < CurrentTime)
13426  // all others have EventTime set
13427  {
13428  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
13429  ", may be before timetable start time");
13430  TrainDataVector.clear();
13431  Utilities->CallLogPop(835);
13432  return(false);
13433  }
13434  CurrentTime = AVEntry.EventTime;
13435  continue;
13436  }
13437  }
13438 
13439  // check locations consistent
13440  AnsiString LastLocationName = "";
13441 
13442  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13443  {
13444  bool LastEntryIsAnArrival = false;
13445  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13446  // first deal with moving Snt entries (all else stopped)
13447  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
13448  {
13449  LastEntryIsAnArrival = false;
13450  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
13451  if(LastLocationName != "")
13452  {
13453  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
13454  }
13455  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
13456  y++) // note that immediate successor to a moving Snt can only be a Moving type
13457  {
13458  // if it's a SignallerControl entry then the condition isn't met
13459  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13460  if(AVEntry.FormatType == Repeat)
13461  {
13462  break; // repeat = reached end (+allows repeat after signaller controlled entry)
13463  }
13464  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13465  {
13466  if(AVEntry.LocationName != LastLocationName)
13467  {
13468  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13469  AVEntry.Command);
13470  TrainDataVector.clear();
13471  Utilities->CallLogPop(823);
13472  return(false);
13473  }
13474  }
13475  else if(AVEntry.FormatType == TimeCmd)
13476  // cdt is the only TimeCmd
13477  {
13478  if(AVEntry.LocationName != LastLocationName)
13479  {
13480  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13481  AVEntry.Command);
13482  TrainDataVector.clear();
13483  Utilities->CallLogPop(824);
13484  return(false);
13485  }
13486  }
13487  else if(AVEntry.FormatType == TimeTimeLoc)
13488  {
13489  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13490  // last entry must be a departure or would have failed earlier
13491  {
13492  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13493  TwoLocationFlag = true;
13494 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13495 // TwoOrMoreLocationsWarningGiven = true;
13496  }
13497  LastLocationName = AVEntry.LocationName;
13498  LastEntryIsAnArrival = false;
13499  }
13500  else if(AVEntry.FormatType == TimeLoc)
13501  {
13502  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13503  {
13504  SecondPassMessage(GiveMessages,
13505  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13506  TrainDataVector.clear();
13507  Utilities->CallLogPop(826);
13508  return(false);
13509  }
13510  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
13511  {
13512  SecondPassMessage(GiveMessages,
13513  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
13514  TrainDataVector.clear();
13515  Utilities->CallLogPop(827);
13516  return(false);
13517  }
13518  LastLocationName = AVEntry.LocationName;
13519  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13520  }
13521  }
13522  }
13523  else // all stationary starting entries
13524  {
13525  LastEntryIsAnArrival = true;
13526  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
13527  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13528  {
13529  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13530  if(AVEntry.FormatType == Repeat)
13531  {
13532  break;
13533  }
13534  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13535  // no need to add anything for shuttle starts since they are at loc (0) anyway
13536  {
13537  if(AVEntry.LocationName != LastLocationName)
13538  {
13539  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13540  AVEntry.Command);
13541  TrainDataVector.clear();
13542  Utilities->CallLogPop(828);
13543  return(false);
13544  }
13545  }
13546  else if(AVEntry.FormatType == TimeCmd)
13547  // cdt is the only TimeCmd
13548  {
13549  if(AVEntry.LocationName != LastLocationName)
13550  {
13551  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13552  AVEntry.Command);
13553  TrainDataVector.clear();
13554  Utilities->CallLogPop(829);
13555  return(false);
13556  }
13557  }
13558  else if(AVEntry.FormatType == TimeTimeLoc)
13559  {
13560  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13561  // last entry must be a departure or would have failed earlier
13562  {
13563  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13564  TwoLocationFlag = true;
13565 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13566 // TwoOrMoreLocationsWarningGiven = true;
13567  }
13568  LastLocationName = AVEntry.LocationName;
13569  LastEntryIsAnArrival = false;
13570  }
13571  else if(AVEntry.FormatType == TimeLoc)
13572  {
13573  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13574  {
13575  SecondPassMessage(GiveMessages,
13576  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13577  TrainDataVector.clear();
13578  Utilities->CallLogPop(831);
13579  return(false);
13580  }
13581  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
13582  {
13583  SecondPassMessage(GiveMessages,
13584  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
13586 // TrainDataVector.clear();
13587 // Utilities->CallLogPop(832);
13588 // return false;
13589  }
13590  LastLocationName = AVEntry.LocationName;
13591  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13592  }
13593  }
13594  }
13595  }
13596 
13597  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
13598  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
13599  AnsiString LocationNameToBeChecked = "";
13600 
13601  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13602  {
13603  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13604  unsigned int y = 0;
13605  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13606  // first discard unlocated Snt entries as they don't have location name set
13607  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13608  {
13609  y = 1;
13610  }
13611  while(y < TDEntry.ActionVector.size())
13612  // need to check each location name separately in turn, skipped for SignallerControl entries
13613  {
13614  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
13615  {
13616  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13617  }
13618  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
13619  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
13620  {
13621  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
13622  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
13623  {
13624  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13625  }
13626  if(AVEntry.Command == "cdt")
13627  {
13628  break; // out of the 'z' loop since the check is only valid up to a change of direction
13629  }
13630  if(AVEntry.LocationName == LocationNameToBeChecked)
13631  {
13632  continue; // keep going while name same
13633  }
13634  if(AVEntry.LocationName != LocationNameToBeChecked)
13635  // if name different check forwards to see if repeats
13636  {
13637  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
13638  {
13639  if(TDEntry.ActionVector.at(a).Command == "cdt")
13640  {
13641  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
13642  }
13643  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13644  {
13645  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13646  TwoLocationFlag = true;
13647 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13648 // TwoOrMoreLocationsWarningGiven = true;
13649  }
13650  }
13651  break; // out of the 'z' loop since have checked 'a' as far as need to
13652  }
13653  }
13654  y++;
13655  }
13656  }
13657  if(TwoLocationFlag)
13658  {
13659  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
13660  TwoLocationList.unique(); //remove duplicates
13661  }
13662 
13663  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
13664  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13665  {
13666  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13667  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13668  {
13669  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13670  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
13671  {
13672  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
13673  }
13674  AnsiString LocName = "";
13675  // dummy, only used so can call IsSNTEntryLocated
13676  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
13677  {
13678  if(AVEntry.LocationName == "")
13679  {
13680  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
13681  }
13682  }
13683  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
13684  {
13685  if(AVEntry.LocationName != "")
13686  {
13687  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
13688  }
13689  }
13690  }
13691  }
13692 
13693 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13694  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13695  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
13696 
13697  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
13698  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
13699 
13700  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
13701  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
13702  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
13703 */
13704  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
13705  {
13706  // non-shuttles & non-repeating links separately, but don't check that there isn't a
13707  // duplicate between a non-repeating shuttle and another - leave original tests in as
13708  // these also set the pointers
13709  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13710  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13711  {
13712  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13713  if(AVEntry.OtherHeadCode != "")
13714  {
13715  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13716  {
13717  Utilities->CallLogPop(1584);
13718  return(false); // error message given in called function
13719  }
13720  }
13721  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13722  {
13723  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13724  {
13725  Utilities->CallLogPop(1585);
13726  return(false); // error message given in called function
13727  }
13728  }
13729  }
13730  }
13731 
13732  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13733  {
13734  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13735  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13736  {
13737  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13738  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13739  {
13740  if(AVEntry.OtherHeadCode != "")
13741  {
13742  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
13743  // false = non-shuttle
13744  {
13745  Utilities->CallLogPop(864);
13746  return(false); // error message given in called function
13747  }
13748  }
13749  }
13750  }
13751  }
13752 
13753  // now repeat the check just for the shuttles
13754  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13755  {
13756  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13757  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13758  {
13759  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13760  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13761  {
13762  if(AVEntry.OtherHeadCode != "")
13763  {
13764  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
13765  // true = shuttle
13766  {
13767  Utilities->CallLogPop(1100);
13768  return(false); // error message given in called function
13769  }
13770  }
13771  }
13772  }
13773  }
13774 
13775  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13776  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13777  {
13778  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13779  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13780  {
13781  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13782  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13783  {
13785  {
13786  Utilities->CallLogPop(1060);
13787  return(false); // error message given in called function
13788  }
13789  }
13790  }
13791  }
13792 
13793  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13794  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13795  // don't ever need to and as designed would skip repeats
13796  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13797  {
13798  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13799  {
13800  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13801  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
13802  {
13803  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
13804  {
13805  Utilities->CallLogPop(1090);
13806  return(false); // error message given in called function
13807  }
13808  }
13809  }
13810  }
13811 
13812  // check all entries have all types set to something
13813  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13814  {
13815  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13816  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13817  {
13818  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13819  if(AVEntry.FormatType == NoFormat)
13820  {
13821  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
13822  }
13823  else if(AVEntry.SequenceType == NoSequence)
13824  {
13825  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
13826  }
13827  else if(AVEntry.LocationType == NoLocation)
13828  {
13829  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
13830  }
13831  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
13832  {
13833  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
13834  }
13835  }
13836  }
13837 
13838  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
13839  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13840  {
13841  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13842  // non-const reference so can alter content
13843  TTrainOperatingData TData;
13844  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
13845  if(LastAVEntry.FormatType == Repeat) // check if a repeat
13846  {
13847 /*
13848  class TTrainOperatingData
13849  {
13850  public:
13851  int TrainID; - default, set at construction
13852  TActionEventType EventReported; used during operation
13853  TRunningEntry RunningEntry; - default, set at construction
13854  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
13855  };
13856 */
13857  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
13858  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
13859  {
13860  TDEntry.TrainOperatingDataVector.push_back(TData);
13861  }
13862  }
13863  else
13864  {
13865  TDEntry.NumberOfTrains = 1;
13866  }
13867  }
13868 
13869  // check that don't include any Continuation names
13870  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13871  {
13872  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13873  {
13874  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13875  AnsiString HC = TrainDataVector.at(x).HeadCode;
13876  if(LocName != "")
13877  {
13878  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
13879  {
13880  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
13881  TrainDataVector.clear();
13882  Utilities->CallLogPop(1578);
13883  return(false);
13884  }
13885  }
13886  }
13887  }
13888 
13889  // check that all repeat times below 96h
13890  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13891  {
13892  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
13893  int IncMinutes = 0;
13894  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
13895  {
13896  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
13897  }
13898  else
13899  {
13900  continue; // basic times already checked in CheckTimeValidity
13901  }
13902  AnsiString HC = TrainDataVector.at(x).HeadCode;
13903  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13904  {
13905  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
13906  {
13907  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13908  {
13909  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
13910  TrainDataVector.clear();
13911  Utilities->CallLogPop(1818);
13912  return(false);
13913  }
13914  }
13915  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
13916  {
13917  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13918  // 3d 23h 59m = 3.9993055556
13919  {
13920  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13921  TrainDataVector.clear();
13922  Utilities->CallLogPop(1819);
13923  return(false);
13924  }
13925  }
13926  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13927  {
13928  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13929  // 3d 23h 59m = 3.9993055556
13930  {
13931  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13932  TrainDataVector.clear();
13933  Utilities->CallLogPop(1820);
13934  return(false);
13935  }
13936  }
13937  }
13938  }
13939 
13940  // Now that all set up change any extended headcodes back to ordinary headcodes
13941  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13942  {
13943  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13944  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13945  {
13946  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13947  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13948  }
13949  }
13950 
13951  // SaveTrainDataVectorToFile(0);//for testing purposes
13953  Utilities->CallLogPop(782);
13954  return(true);
13955 }
13956 
13957 // ---------------------------------------------------------------------------
13958 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13960 {
13961  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13962 }
13963 
13964 // ---------------------------------------------------------------------------
13965 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13967 {
13968  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13969  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13970  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13971 }
13972 
13973 // ---------------------------------------------------------------------------
13974 
13975 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13976 {
13977  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13978  if(HeadCode.Length() > 4) // ignore otherwise
13979  {
13980  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13981  }
13982  Utilities->CallLogPop(1593);
13983 }
13984 
13985 // ---------------------------------------------------------------------------
13986 
13987 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13988 {
13989  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13990  SecondHeadCode);
13991  int ForwardCount = 0;
13992  int ReverseCount = 0;
13993 
13994  if(MainHeadCode == SecondHeadCode)
13995  {
13996  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13997  TrainDataVector.clear();
13998  Utilities->CallLogPop(1594);
13999  return(false);
14000  }
14001  // forward check
14002  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14003  {
14004  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14005  if(TDEntry.HeadCode == MainHeadCode)
14006  {
14007  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14008  {
14009  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14010  if(AVEntry.OtherHeadCode == SecondHeadCode)
14011  {
14012  ForwardCount++;
14013  }
14014  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
14015  // need own check in case both 'Other' & 'NonRepeating' have same headcode
14016  {
14017  ForwardCount++;
14018  }
14019  }
14020  }
14021  }
14022  if(ForwardCount == 0)
14023  // this is an exception because the headcodes are selected in the same order as the forward check
14024  {
14025  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
14026  }
14027  if(ForwardCount > 2)
14028  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
14029  {
14030  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
14031  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14032  TrainDataVector.clear();
14033  Utilities->CallLogPop(1587);
14034  return(false);
14035  }
14036  // reverse check
14037  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14038  {
14039  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14040  if(TDEntry.HeadCode == SecondHeadCode)
14041  {
14042  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14043  {
14044  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14045  if(AVEntry.OtherHeadCode == MainHeadCode)
14046  {
14047  ReverseCount++;
14048  }
14049  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14050  {
14051  ReverseCount++;
14052  }
14053  }
14054  }
14055  }
14056 
14057  if(ReverseCount == 0)
14058  {
14059  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
14060  TrainDataVector.clear();
14061  Utilities->CallLogPop(1588);
14062  return(false);
14063  }
14064  if(ReverseCount > 2)
14065  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
14066  {
14067  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
14068  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14069  TrainDataVector.clear();
14070  Utilities->CallLogPop(1589);
14071  return(false);
14072  }
14073  if(ForwardCount != ReverseCount)
14074  {
14075  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
14076  " than the other way round");
14077  TrainDataVector.clear();
14078  Utilities->CallLogPop(1610);
14079  return(false);
14080  }
14081  Utilities->CallLogPop(1590);
14082  return(true);
14083 }
14084 
14085 // ---------------------------------------------------------------------------
14086 
14087 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
14088 /* Return false for no find or more than one find, check correct types of link
14089  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
14090  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
14091  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
14092  Then do the same in reverse.
14093  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
14094  if main is Fns other must be Sns; if main is jbo other must be Fjo.
14095  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
14096  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
14097  for Sfs & Sns services. Finally check the repeat entries if present are consistent
14098 
14099  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
14100  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
14101 
14102  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
14103 */
14104 
14105 {
14106  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
14107  int ForwardCount = 0;
14108  int ReverseCount = 0;
14109  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14110  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14111  TTrainDataEntry *MainTrainDataPtr = 0;
14112  TTrainDataEntry *OtherTrainDataPtr = 0;
14113 
14114  // forward check
14115  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14116  {
14117  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14118  if(TDEntry.HeadCode == MainHeadCode)
14119  {
14120  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14121  {
14122  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14123  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14124  {
14125  if(AVEntry.OtherHeadCode == OtherHeadCode)
14126  {
14127  MainTrainDataPtr = &TrainDataVector.at(x);
14128  ForwardEntryPtr = &AVEntry;
14129  ForwardCount++;
14130  ForwardTDVectorNumber = x;
14131  }
14132  }
14133  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
14134  (AVEntry.Command == "Frh-sh")))
14135  {
14136  if(AVEntry.OtherHeadCode == OtherHeadCode)
14137  {
14138  MainTrainDataPtr = &TrainDataVector.at(x);
14139  ForwardEntryPtr = &AVEntry;
14140  ForwardCount++;
14141  ForwardTDVectorNumber = x;
14142  }
14143  }
14144  }
14145  }
14146  }
14147  if(ForwardCount == 0)
14148  // this is an exception because the headcodes are selected in the same order as the forward check
14149  {
14150  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
14151  }
14152  if(ForwardCount > 1)
14153  {
14154  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
14155  MainHeadCode);
14156  TrainDataVector.clear();
14157  Utilities->CallLogPop(836);
14158  return(false);
14159  }
14160  // reverse check
14161  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14162  {
14163  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14164  if(TDEntry.HeadCode == OtherHeadCode)
14165  {
14166  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14167  {
14168  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14169  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14170  {
14171  if(AVEntry.OtherHeadCode == MainHeadCode)
14172  {
14173  OtherTrainDataPtr = &TrainDataVector.at(x);
14174  ReverseCount++;
14175  ReverseEntryPtr = &AVEntry;
14176  ReverseTDVectorNumber = x;
14177  }
14178  }
14179  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
14180  {
14181  if(AVEntry.OtherHeadCode == MainHeadCode)
14182  {
14183  OtherTrainDataPtr = &TrainDataVector.at(x);
14184  ReverseCount++;
14185  ReverseEntryPtr = &AVEntry;
14186  ReverseTDVectorNumber = x;
14187  }
14188  }
14189  }
14190  }
14191  }
14192 
14193  if(ReverseCount == 0)
14194  {
14195  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
14196  TrainDataVector.clear();
14197  Utilities->CallLogPop(837);
14198  return(false);
14199  }
14200  if(ReverseCount > 1)
14201  {
14202  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14203  OtherHeadCode);
14204  TrainDataVector.clear();
14205  Utilities->CallLogPop(838);
14206  return(false);
14207  }
14208  // these will all be false for !Shuttle
14209  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
14210  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
14211  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
14212  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
14213 
14214  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
14215  {
14216  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
14217  TrainDataVector.clear();
14218  Utilities->CallLogPop(1058);
14219  return(false);
14220  }
14221  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
14222  {
14223  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
14224  TrainDataVector.clear();
14225  Utilities->CallLogPop(1059);
14226  return(false);
14227  }
14228  if(ForwardEntryPtr->LocationName == "")
14229  {
14230  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
14231  ". One or other service does not have a location set");
14232  TrainDataVector.clear();
14233  Utilities->CallLogPop(526);
14234  return(false);
14235  }
14236  if(ReverseEntryPtr->LocationName == "")
14237  {
14238  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
14239  ". One or other service does not have a location set");
14240  TrainDataVector.clear();
14241  Utilities->CallLogPop(527);
14242  return(false);
14243  }
14244  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14245  {
14246  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
14247  " is at a different location to the referencing train " + MainHeadCode);
14248  TrainDataVector.clear();
14249  Utilities->CallLogPop(842);
14250  return(false);
14251  }
14252  // ignore shuttle repeat links for first time check
14253  if(!Shuttle)
14254  {
14255  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14256  {
14257  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
14258  " has a different event time to the referencing train " + MainHeadCode);
14259  TrainDataVector.clear();
14260  Utilities->CallLogPop(525);
14261  return(false);
14262  }
14263  }
14264  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
14265  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
14266  if(ForwardShuttleStart && ReverseShuttleFinish)
14267  // Shuttle must be true if these are true
14268  {
14269  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
14270  {
14271  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
14272  " first repeat restart time not consistent with finish service " + OtherHeadCode);
14273  TrainDataVector.clear();
14274  Utilities->CallLogPop(1055);
14275  return(false);
14276  }
14277  }
14278  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
14279  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
14280  {
14281  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14282  {
14283  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
14284  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14285  TrainDataVector.clear();
14286  Utilities->CallLogPop(528);
14287  return(false);
14288  }
14289  }
14290  if(ReverseEntryPtr->Command == "Fjo")
14291  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
14292  {
14293  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14294  {
14295  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
14296  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14297  TrainDataVector.clear();
14298  Utilities->CallLogPop(862);
14299  return(false);
14300  }
14301  }
14302  if(ReverseEntryPtr->Command == "Fns")
14303  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
14304  {
14305  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14306  {
14307  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
14308  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14309  TrainDataVector.clear();
14310  Utilities->CallLogPop(529);
14311  return(false);
14312  }
14313  }
14314  if(ForwardEntryPtr->Command == "Sfs")
14315  {
14316  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
14317  {
14318  SecondPassMessage(GiveMessages,
14319  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
14320  MainHeadCode);
14321  TrainDataVector.clear();
14322  Utilities->CallLogPop(530);
14323  return(false);
14324  }
14325  }
14326  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
14327  {
14328  if(ReverseEntryPtr->Command != "Sfs")
14329  {
14330  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
14331  MainHeadCode);
14332  TrainDataVector.clear();
14333  Utilities->CallLogPop(839);
14334  return(false);
14335  }
14336  else
14337  {
14338  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
14339  {
14340  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
14341  TrainDataVector.clear();
14342  Utilities->CallLogPop(849);
14343  return(false);
14344  }
14345  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
14346  {
14347  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
14348  TrainDataVector.clear();
14349  Utilities->CallLogPop(850);
14350  return(false);
14351  }
14352  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
14353  {
14354  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
14355  TrainDataVector.clear();
14356  Utilities->CallLogPop(846);
14357  return(false);
14358  }
14359  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14360  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14361  if(OtherTrainDataPtr->Description == "")
14362  {
14363  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14364  }
14365  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
14366  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14367  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14368  }
14369  }
14370  if(ForwardEntryPtr->Command == "Sns")
14371  {
14372  if(ReverseEntryPtr->Command != "Fns")
14373  {
14374  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
14375  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
14376  TrainDataVector.clear();
14377  Utilities->CallLogPop(531);
14378  return(false);
14379  }
14380  }
14381  if(ForwardEntryPtr->Command == "Fns")
14382  {
14383  if(ReverseEntryPtr->Command != "Sns")
14384  {
14385  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
14386  " and forms a new service with headcode " + OtherHeadCode);
14387  TrainDataVector.clear();
14388  Utilities->CallLogPop(840);
14389  return(false);
14390  }
14391  else
14392  {
14393  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14394  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14395  if(OtherTrainDataPtr->Description == "")
14396  {
14397  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14398  }
14399  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14400  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14401  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14402  }
14403  }
14404  if(ForwardEntryPtr->Command == "jbo")
14405  {
14406  if(ReverseEntryPtr->Command != "Fjo")
14407  {
14408  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
14409  " and is joined by a train with headcode " + OtherHeadCode);
14410  TrainDataVector.clear();
14411  Utilities->CallLogPop(841);
14412  return(false);
14413  }
14414  }
14415  if(ForwardEntryPtr->Command == "Fjo")
14416  {
14417  if(ReverseEntryPtr->Command != "jbo")
14418  {
14419  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
14420  " and joins a train with headcode " + OtherHeadCode);
14421  TrainDataVector.clear();
14422  Utilities->CallLogPop(532);
14423  return(false);
14424  }
14425  else
14426  {
14427  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14428  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14429  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
14430  {
14431  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14432  }
14433  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
14434  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
14435  }
14436  }
14437  if(ForwardShuttleStart)
14438  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
14439  {
14440  if(!ReverseShuttleFinish)
14441  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
14442  {
14443  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
14444  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
14445  TrainDataVector.clear();
14446  Utilities->CallLogPop(1056);
14447  return(false);
14448  }
14449  }
14450  if(ReverseShuttleStart)
14451  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
14452  {
14453  if(!ForwardShuttleFinish)
14454  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
14455  {
14456  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
14457  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
14458  TrainDataVector.clear();
14459  Utilities->CallLogPop(1057);
14460  return(false);
14461  }
14462  else
14463  {
14464  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14465  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14466 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
14467  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
14468 */
14469  }
14470  }
14471  // check repeat information consistent if present
14472  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
14473  // and those not accessed here
14474 
14475  // still need to check the non-repeating links and that they have no repeats - do that outside this function
14476  bool MainRepeat = false, OtherRepeat = false;
14477  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
14478 
14479  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14480  {
14481  MainRepeat = true;
14482  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
14483  }
14484  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14485  {
14486  OtherRepeat = true;
14487  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
14488  }
14489  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
14490  {
14491  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
14492  " and the associated train with headcode " + OtherHeadCode);
14493  TrainDataVector.clear();
14494  Utilities->CallLogPop(844);
14495  return(false);
14496  }
14497  if(MainRepeat && OtherRepeat)
14498  {
14499  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
14500  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
14501  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
14502  {
14503  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
14504  " and the associated train with headcode " + OtherHeadCode);
14505  TrainDataVector.clear();
14506  Utilities->CallLogPop(845);
14507  return(false);
14508  }
14509  }
14510  Utilities->CallLogPop(863);
14511  return(true);
14512 }
14513 
14514 // ---------------------------------------------------------------------------
14515 
14516 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
14517 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
14518 {
14519  // strip spaces from extreme ends of input
14520  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
14521  if(Input == "")
14522  {
14523  Utilities->CallLogPop(856);
14524  return;
14525  }
14526  while(Input[1] == ' ')
14527  {
14528  if(Input.Length() > 1)
14529  {
14530  Input = Input.SubString(2, Input.Length() - 1);
14531  }
14532  else
14533  {
14534  Input = "";
14535  Utilities->CallLogPop(857);
14536  return;
14537  }
14538  }
14539  if(Input == "")
14540  {
14541  Utilities->CallLogPop(858);
14542  return;
14543  }
14544  while(Input[Input.Length()] == ' ')
14545  {
14546  if(Input.Length() > 1)
14547  {
14548  Input = Input.SubString(1, Input.Length() - 1);
14549  }
14550  else
14551  {
14552  Input = "";
14553  Utilities->CallLogPop(859);
14554  return;
14555  }
14556  }
14557  // now strip spaces immediately after all commas and semicolons within the text
14558  AnsiString Output = "";
14559  bool DelimiterFound = false;
14560 
14561  for(int x = 1; x < Input.Length() + 1; x++)
14562  {
14563  if(DelimiterFound)
14564  {
14565  if(Input[x] == ' ')
14566  {
14567  continue;
14568  }
14569  }
14570  if((Input[x] != ',') && (Input[x] != ';'))
14571  {
14572  DelimiterFound = false;
14573  Output = Output + Input[x];
14574  }
14575  else
14576  {
14577  DelimiterFound = true;
14578  Output = Output + Input[x];
14579  }
14580  }
14581  if(Output == "")
14582  {
14583  Input = "";
14584  Utilities->CallLogPop(860);
14585  return;
14586  }
14587  // now strip spaces immediately before all commas and semicolons within the text
14588  Input = Output;
14589  Output = "";
14590  DelimiterFound = false;
14591  for(int x = Input.Length(); x > 0; x--)
14592  {
14593  if(DelimiterFound)
14594  {
14595  if(Input[x] == ' ')
14596  {
14597  continue;
14598  }
14599  }
14600  if((Input[x] != ',') && (Input[x] != ';'))
14601  {
14602  DelimiterFound = false;
14603  Output = AnsiString(Input[x]) + Output;
14604  }
14605  else
14606  {
14607  DelimiterFound = true;
14608  Output = AnsiString(Input[x]) + Output;
14609  }
14610  }
14611  Input = Output;
14612  Utilities->CallLogPop(861);
14613 }
14614 
14615 // ---------------------------------------------------------------------------
14616 
14617 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
14618 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
14619 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
14620 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
14621 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
14622 // are done in this function, they must be done elsewhere.
14623 //a starting speed > 0 always returns false
14624 {
14625  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
14626  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14627  LocationName = "";
14628  if(TDEntry.StartSpeed > 0)
14629  {
14630  Utilities->CallLogPop(1784);
14631  return(false);
14632  }
14633  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
14634  {
14635  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
14636  }
14638  {
14639  Utilities->CallLogPop(852);
14640  return(false);
14641  }
14642  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
14643  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
14644 
14645  if(LocRear != "")
14646  {
14647  LocationName = LocRear;
14648  }
14649  else
14650  {
14651  LocationName = LocFront;
14652  }
14653  if(LocationName == "")
14654  {
14655  Utilities->CallLogPop(1036);
14656  return(false);
14657  }
14658  if(AVEntry0.SignallerControl)
14659  {
14660  Utilities->CallLogPop(1773);
14661  return(true);
14662  }
14663 // here if not a signaller start entry so must be at least one more entry, and it is at a location
14664 
14665 //Ok Not ok continue
14666 
14667 //Frh if Snt Frh-sh cdt
14668 //Fns if Snt Fns-sh fsp or rsp
14669 //Fjo if Snt TimeTimeLoc jbo
14670 //F-nshs if Snt pas
14671 //TimeLoc dep Fer
14672 // TimeLoc arr
14673 
14674  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
14675  {
14676  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
14677  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
14678  {
14679  Utilities->CallLogPop(1037);
14680  return(true);
14681  }
14682  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
14683  {
14684  Utilities->CallLogPop(2442);
14685  return(true);
14686  }
14687  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
14688  {
14689  Utilities->CallLogPop(2438);
14690  return(false);
14691  }
14692  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
14693  {
14694  Utilities->CallLogPop(854);
14695  return(false);
14696  }
14697  if((AVEntry.Command == "cdt") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
14698  {
14699  continue;
14700  }
14701  }
14702  Utilities->CallLogPop(855);
14703  return(false);
14704 
14705 }
14706 
14707 // ---------------------------------------------------------------------------
14708 
14709 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
14710 {
14711  // checks that the new train start elements are valid - both exist & are connected, and that not
14712  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
14713  // & not starting with front on a continuation
14714  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
14715  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
14716 
14717  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
14718  if(RearPosition < 0)
14719  // error message given in GetTrackVectorPositionFromString
14720  {
14721  Utilities->CallLogPop(759);
14722  return(false);
14723  }
14724  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
14725  if(FrontPosition < 0)
14726  // error message given in GetTrackVectorPositionFromString
14727  {
14728  Utilities->CallLogPop(760);
14729  return(false);
14730  }
14731  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
14732  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
14733  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
14734 
14735  // check front & rear connected
14736  for(int x = 0; x < 4; x++)
14737  {
14738  if(RearTrackElement.Conn[x] == FrontPosition)
14739  {
14740  RearExitPos = x;
14741  break;
14742  }
14743  if(x == 3)
14744  {
14745  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
14746  Utilities->CallLogPop(762);
14747  return(false);
14748  }
14749  }
14750  // check not starting with front on a continuation
14751  if(FrontType == Continuation)
14752  {
14753  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
14754  Utilities->CallLogPop(937);
14755  return(false);
14756  }
14757  // check not starting on a level crossing
14758  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
14759  {
14760  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
14761  Utilities->CallLogPop(1951);
14762  return(false);
14763  }
14764  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
14765  {
14766  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
14767  Utilities->CallLogPop(1952);
14768  return(false);
14769  }
14770  // check if trying to start on diverging leg of points
14771  if((RearType == Points) && (RearExitPos == 3))
14772  {
14773  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
14774  Utilities->CallLogPop(936);
14775  return(false);
14776  }
14777  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
14778  {
14779  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
14780  Utilities->CallLogPop(1808);
14781  return(false);
14782  }
14783  Utilities->CallLogPop(905);
14784  return(true);
14785 }
14786 
14787 // ---------------------------------------------------------------------------
14788 
14789 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
14790 // Rear & front element validity already checked in CheckStartPositionValidity
14791 // This checks for points in correct orientation, no train at start position and not starting on a locked route
14792 {
14793  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
14794  AnsiString(RearExitPos));
14795  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
14796 
14797  if(RearTrackElement.TrackType == Continuation)
14798  {
14799  EventType = FailTrainEntry;
14800  }
14801  else
14802  {
14803  EventType = FailCreateTrain;
14804  }
14805  int FrontPosition = RearTrackElement.Conn[RearExitPos];
14806  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
14807  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
14808  TTrackType RearType = RearTrackElement.TrackType;
14809  TTrackType FrontType = FrontTrackElement.TrackType;
14810  AnsiString RearName, FrontName;
14811 
14812  if(RearTrackElement.ActiveTrackElementName != "")
14813  {
14814  RearName = RearTrackElement.ActiveTrackElementName;
14815  }
14816  else
14817  {
14818  RearName = RearTrackElement.ElementID;
14819  }
14820  if(FrontTrackElement.ActiveTrackElementName != "")
14821  {
14822  FrontName = FrontTrackElement.ActiveTrackElementName;
14823  }
14824  else
14825  {
14826  FrontName = FrontTrackElement.ElementID;
14827  }
14828  TPrefDirElement PrefDirElement; // needed for next function but not used
14829  int LockedVectorNumber; // needed for next function but not used
14830 
14831  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
14832  {
14833  if(ReportFlag)
14834  {
14835  if(EventType == FailCreateTrain)
14836  {
14837  EventType = FailCreateLockedRoute;
14838  }
14839  else
14840  {
14841  EventType = FailEnterLockedRoute;
14842  }
14843  LogActionError(47, HeadCode, "", EventType, FrontName);
14844  }
14845  Utilities->CallLogPop(940);
14846  return(false);
14847  }
14848  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
14849  {
14850  if(ReportFlag)
14851  {
14852  if(EventType == FailCreateTrain)
14853  {
14854  EventType = FailCreateLockedRoute;
14855  }
14856  else
14857  {
14858  EventType = FailEnterLockedRoute;
14859  }
14860  LogActionError(48, HeadCode, "", EventType, RearName);
14861  }
14862  Utilities->CallLogPop(1809);
14863  return(false);
14864  }
14865  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
14866  {
14867  if(ReportFlag)
14868  {
14869  LogActionError(27, HeadCode, "", EventType, RearName);
14870  }
14871  Utilities->CallLogPop(1810);
14872  return(false);
14873  }
14874  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
14875  {
14876  if(ReportFlag)
14877  {
14878  if(EventType == FailCreateTrain)
14879  {
14880  LogActionError(28, HeadCode, "", EventType, FrontName);
14881  }
14882  else
14883  {
14884  LogActionError(43, HeadCode, "", EventType, RearName);
14885  }
14886  }
14887  Utilities->CallLogPop(941);
14888  return(false);
14889  }
14890  if(RearType == Bridge)
14891  {
14892  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
14893  {
14894  if(ReportFlag)
14895  {
14896  LogActionError(29, HeadCode, "", EventType, RearName);
14897  }
14898  Utilities->CallLogPop(942);
14899  return(false);
14900  }
14901  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
14902  {
14903  if(ReportFlag)
14904  {
14905  LogActionError(30, HeadCode, "", EventType, RearName);
14906  }
14907  Utilities->CallLogPop(943);
14908  return(false);
14909  }
14910  }
14911  if(FrontType == Bridge)
14912  {
14913  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
14914  {
14915  if(ReportFlag)
14916  {
14917  if(EventType == FailCreateTrain)
14918  {
14919  LogActionError(31, HeadCode, "", EventType, FrontName);
14920  }
14921  else
14922  {
14923  LogActionError(44, HeadCode, "", EventType, RearName);
14924  }
14925  }
14926  Utilities->CallLogPop(944);
14927  return(false);
14928  }
14929  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
14930  {
14931  if(ReportFlag)
14932  {
14933  if(EventType == FailCreateTrain)
14934  {
14935  LogActionError(45, HeadCode, "", EventType, FrontName);
14936  }
14937  else
14938  {
14939  LogActionError(46, HeadCode, "", EventType, RearName);
14940  }
14941  }
14942  Utilities->CallLogPop(945);
14943  return(false);
14944  }
14945  }
14946  EventType = FailCreatePoints;
14947  if(RearType == Points)
14948  {
14949  if(RearTrackElement.Attribute == 1)
14950  {
14951  if(ReportFlag)
14952  {
14953  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14954  }
14955  Utilities->CallLogPop(933);
14956  return(false);
14957  }
14958  }
14959  if(FrontType == Points)
14960  {
14961  if(FrontTrackElement.Attribute == 1)
14962  {
14963  if(ReportFlag)
14964  {
14965  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14966  }
14967  Utilities->CallLogPop(934);
14968  return(false);
14969  }
14970  }
14971 
14972  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14973  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14974  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14975  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14976  int RouteNumber; //not used
14977  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14978  {
14979  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14980  {
14981  EventType = FailEntryRouteSetAgainst;
14982  if(ReportFlag)
14983  {
14984  LogActionError(63, HeadCode, "", EventType, RearName);
14985  }
14986  Utilities->CallLogPop(2317);
14987  return(false);
14988  }
14989  }
14990  Utilities->CallLogPop(939);
14991  return(true);
14992 }
14993 
14994 // ---------------------------------------------------------------------------
14995 
14996 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14997 {
14998  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14999  "," + AnsiString(IncDigits));
15000  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
15001  {
15002  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
15003  }
15004  if(!Last2CharactersBothDigits(2, BaseHeadCode))
15005  {
15006  Utilities->CallLogPop(1893);
15007  return(BaseHeadCode);
15008  }
15009  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
15010  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
15011 
15012  while(NextRepeatDigits >= 100)
15013  {
15014  NextRepeatDigits -= 100; // rolls over after 99
15015  }
15016  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
15017 
15018  if(NextRepeatDigitsStr.Length() < 2)
15019  {
15020  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
15021  }
15022  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
15023 
15024  Utilities->CallLogPop(1365);
15025  return(NextRepeatHeadCode);
15026 }
15027 
15028 // ---------------------------------------------------------------------------
15029 
15030 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
15031 {
15032  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
15033  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
15034  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
15035  Utilities->CallLogPop(1366);
15036  return(NextRepeatTime);
15037 }
15038 
15039 // ---------------------------------------------------------------------------
15040 
15041 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
15042 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
15043 {
15044  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
15045  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
15046  int ForwardSecs = int(double(ForwardEventTime) * 86400);
15047  int ReverseSecs = int(double(ReverseEventTime) * 86400);
15048  int RepeatSecs = RepeatMinutes * 60;
15049 
15050  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
15051  {
15052  Utilities->CallLogPop(1367);
15053  return(false);
15054  }
15055  else
15056  {
15057  Utilities->CallLogPop(1368);
15058  return(true);
15059  }
15060 }
15061 
15062 // ---------------------------------------------------------------------------
15063 
15064 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
15065 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
15066 
15067 /* Double crosslink (shuttle) table:
15068 
15069 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
15070  Code ShuttleLink- EntryPtr ShuttleLink-
15071  HeadCode EntryPtr
15072 
15073 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
15074 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
15075 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
15076 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
15077 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
15078 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
15079 
15080 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
15081 */
15082 
15083 {
15084  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
15085  NonRepeatingHeadCode);
15086  int ForwardCount = 0;
15087  int ReverseCount = 0;
15088  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
15089  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
15090  // Forward corresponds to Main, Reverse to Other
15091  TTrainDataEntry *MainTrainDataPtr = 0;
15092  TTrainDataEntry *OtherTrainDataPtr = 0;
15093 
15094  // forward check
15095  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15096  {
15097  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15098  if(TDEntry.HeadCode == MainHeadCode)
15099  {
15100  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15101  {
15102  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15103  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
15104  {
15105  MainTrainDataPtr = &TrainDataVector.at(x);
15106  ForwardEntryPtr = &AVEntry;
15107  ForwardCount++;
15108  ForwardTDVectorNumber = x;
15109  }
15110  }
15111  }
15112  }
15113  if(ForwardCount == 0)
15114  // this is an exception because the headcodes are selected in the same order as the forward check
15115  {
15116  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
15117  }
15118  if(ForwardCount > 1)
15119  {
15120  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
15121  MainHeadCode);
15122  TrainDataVector.clear();
15123  Utilities->CallLogPop(1061);
15124  return(false);
15125  }
15126  // reverse check
15127  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15128  {
15129  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15130  if(TDEntry.HeadCode == NonRepeatingHeadCode)
15131  {
15132  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15133  {
15134  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15135  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
15136  {
15137  OtherTrainDataPtr = &TrainDataVector.at(x);
15138  ReverseCount++;
15139  ReverseEntryPtr = &AVEntry;
15140  ReverseTDVectorNumber = x;
15141  }
15142  }
15143  }
15144  }
15145 
15146  if(ReverseCount == 0)
15147  {
15148  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
15149  TrainDataVector.clear();
15150  Utilities->CallLogPop(1062);
15151  return(false);
15152  }
15153  if(ReverseCount > 1)
15154  {
15155  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
15156  NonRepeatingHeadCode);
15157  TrainDataVector.clear();
15158  Utilities->CallLogPop(1063);
15159  return(false);
15160  }
15161  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
15162  {
15163  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
15164  TrainDataVector.clear();
15165  Utilities->CallLogPop(1064);
15166  return(false);
15167  }
15168  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
15169  {
15170  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
15171  TrainDataVector.clear();
15172  Utilities->CallLogPop(1065);
15173  return(false);
15174  }
15175  if(ForwardEntryPtr->LocationName == "")
15176  {
15177  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
15178  ". One or other service does not have a location set");
15179  TrainDataVector.clear();
15180  Utilities->CallLogPop(1066);
15181  return(false);
15182  }
15183  if(ReverseEntryPtr->LocationName == "")
15184  {
15185  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
15186  ". One or other service does not have a location set");
15187  TrainDataVector.clear();
15188  Utilities->CallLogPop(1067);
15189  return(false);
15190  }
15191  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
15192  {
15193  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
15194  " is at a different location to the referencing train " + MainHeadCode);
15195  TrainDataVector.clear();
15196  Utilities->CallLogPop(1068);
15197  return(false);
15198  }
15199  if(ForwardEntryPtr->Command == "F-nshs")
15200  // i.e. the non repeating link into the shuttle service
15201  {
15202  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
15203  {
15204  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
15205  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
15206  TrainDataVector.clear();
15207  Utilities->CallLogPop(1069);
15208  return(false);
15209  }
15210  }
15211  if(ForwardEntryPtr->Command == "Fns-sh")
15212  // i.e. the non repeating link out from the shuttle service
15213  {
15214  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
15215  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
15216  {
15217  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
15218  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
15219  TrainDataVector.clear();
15220  Utilities->CallLogPop(1070);
15221  return(false);
15222  }
15223  }
15224  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
15225  // i.e. a non repeating link to or from the shuttle service
15226  {
15227  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15228  {
15229  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
15230  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
15231  TrainDataVector.clear();
15232  Utilities->CallLogPop(1071);
15233  return(false);
15234  }
15235  }
15236 /* it's allowed to have a different description
15237  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
15238  {
15239  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
15240  {
15241  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
15242  TrainDataVector.clear();
15243  Utilities->CallLogPop(1072);
15244  return false;
15245  }
15246  }
15247 */
15248  if(ForwardEntryPtr->Command == "Sns-sh")
15249  {
15250  if(ReverseEntryPtr->Command != "F-nshs")
15251  {
15252  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
15253  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
15254  TrainDataVector.clear();
15255  Utilities->CallLogPop(1073);
15256  return(false);
15257  }
15258  }
15259  if(ForwardEntryPtr->Command == "F-nshs")
15260  {
15261  if(ReverseEntryPtr->Command != "Sns-sh")
15262  {
15263  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
15264  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
15265  TrainDataVector.clear();
15266  Utilities->CallLogPop(1074);
15267  return(false);
15268  }
15269  else
15270  {
15271  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15272  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
15273  if(OtherTrainDataPtr->Description == "")
15274  {
15275  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15276  }
15277  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15278  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15279  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15280  }
15281  }
15282  if(ForwardEntryPtr->Command == "Sns-fsh")
15283  {
15284  if(ReverseEntryPtr->Command != "Fns-sh")
15285  {
15286  SecondPassMessage(GiveMessages,
15287  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
15288  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
15289  TrainDataVector.clear();
15290  Utilities->CallLogPop(1075);
15291  return(false);
15292  }
15293  }
15294  if(ForwardEntryPtr->Command == "Fns-sh")
15295  {
15296  if(ReverseEntryPtr->Command != "Sns-fsh")
15297  {
15298  SecondPassMessage(GiveMessages,
15299  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
15300  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
15301  TrainDataVector.clear();
15302  Utilities->CallLogPop(1076);
15303  return(false);
15304  }
15305  else
15306  {
15307  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
15308  // links to the non-repeating non-shuttle linked service
15309  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15310  // needed for creating formatted timetable
15311  if(OtherTrainDataPtr->Description == "")
15312  {
15313  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15314  }
15315  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15316  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15317  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15318  }
15319  }
15320  Utilities->CallLogPop(1077);
15321  return(true);
15322 }
15323 
15324 // ---------------------------------------------------------------------------
15325 
15326 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
15327 // Forward train is the finish shuttle entry 'Fns-sh'.
15328 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
15329 {
15330  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
15331  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
15332  int ForwardSecs = int(double(ForwardEventTime) * 86400);
15333  int ReverseSecs = int(double(ReverseEventTime) * 86400);
15334  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
15335 
15336  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
15337  {
15338  Utilities->CallLogPop(1369);
15339  return(false);
15340  }
15341  else
15342  {
15343  Utilities->CallLogPop(1370);
15344  return(true);
15345  }
15346 }
15347 
15348 // ---------------------------------------------------------------------------
15349 
15350 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
15351 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
15352 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
15353 // don't ever need to and as designed would skip repeats.
15354 
15355 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
15356 {
15357  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
15358  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
15359  {
15360  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
15361  }
15362  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
15363  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
15364  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
15365 
15366  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
15367  {
15368  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
15369  TrainDataVector.clear();
15370  Utilities->CallLogPop(1091);
15371  return(false);
15372  }
15373  while(LastActionCommand == "Fns")
15374  {
15375  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
15376  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
15377  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
15378  {
15379  SecondPassMessage(GiveMessages,
15380  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
15381  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
15382  TrainDataVector.clear();
15383  Utilities->CallLogPop(1092);
15384  return(false);
15385  }
15386  }
15387  // exit the 'while' with LastActionCommand FSH-XX
15388  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
15389  {
15390  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
15391  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
15392  ". The linking of two or more shuttles is not permitted.");
15393  TrainDataVector.clear();
15394  Utilities->CallLogPop(1093);
15395  return(false);
15396  }
15397  Utilities->CallLogPop(1094);
15398  return(true);
15399 }
15400 
15401 // ---------------------------------------------------------------------------
15402 
15403 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
15404 {
15405  if(!GiveMessages)
15406  {
15407  return;
15408  }
15409  // if(ServiceReference == "") ShowMessage(Message);
15410  if(!CheckHeadCodeValidity(12, false, ServiceReference))
15411  {
15412  ShowMessage(Message);
15413  }
15414  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
15415  // false means don't give messages within the function
15416  else
15417  {
15418  ShowMessage("Service " + ServiceReference + ": " + Message);
15419  }
15420 }
15421 
15422 // ---------------------------------------------------------------------------
15423 
15424 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
15425 {
15426  if(!GiveMessages)
15427  {
15428  return;
15429  }
15430  ShowMessage(Message);
15431 }
15432 
15433 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
15434 // ---------------------------------------------------------------------------
15435 
15436 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
15437 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
15438 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
15439 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
15440 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
15441 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
15442 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
15443 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
15444 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
15445 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
15446 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
15447 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
15448 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
15449 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
15450 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
15451 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
15452 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
15453 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
15454 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
15455 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
15456 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
15457 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
15458 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
15459 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
15460 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
15461 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
15462 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
15463 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
15464 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
15465 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
15466 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
15467 {
15468  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
15469  AnsiString(ActionEventType) + "," + LocationID);
15470  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
15471 
15472  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
15473  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode; //added at v2.9.1 to give more info to user
15474 
15475  Prefix = " ERROR: ";
15476  if(ActionEventType == FailTrainEntry)
15477  {
15478  Prefix = " HELD: ";
15479  ErrorLog = " can't enter railway, train obstructing entry position ";
15480  WarningStr = " can't enter railway, train obstructing entry position ";
15481  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
15482  }
15483  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
15484  {
15485  Prefix = " HELD: ";
15486  ErrorLog = " can't enter railway, route set against it at entry position ";
15487  WarningStr = " can't enter railway, route set against it at entry position ";
15488  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
15489  }
15490  else if(ActionEventType == FailCreateTrain)
15491  {
15492  Prefix = " HELD: ";
15493  ErrorLog = " can't be created, train obstructing ";
15494  WarningStr = " can't be created, train obstructing ";
15495  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
15496  }
15497  else if(ActionEventType == FailCreateLockedRoute)
15498  {
15499  Prefix = " HELD: ";
15500  ErrorLog = " can't be created on a locked route at ";
15501  WarningStr = " can't be created on a locked route at ";
15502  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
15503  }
15504  else if(ActionEventType == FailEnterLockedRoute)
15505  {
15506  Prefix = " HELD: ";
15507  ErrorLog = " can't enter on a locked route at ";
15508  WarningStr = " can't enter on a locked route at ";
15509  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
15510  }
15511  else if(ActionEventType == FailCreatePoints)
15512  {
15513  Prefix = " HELD: ";
15514  ErrorLog = " can't be created, diverging points at ";
15515  WarningStr = " can't be created, diverging points at ";
15516  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
15517  }
15518  else if(ActionEventType == FailUnexpectedExitRailway)
15519  {
15520  ErrorLog = " left railway unexpectedly at ";
15521  UnexpectedExits++;
15522  }
15523  else if(ActionEventType == FailIncorrectExit)
15524  {
15525  ErrorLog = " left railway at an incorrect exit at ";
15526  IncorrectExits++;
15527  }
15528  else if(ActionEventType == FailLocTooShort)
15529  {
15530  ErrorLog = " failed to split - location too short at ";
15531  WarningStr = " failed to split, location too short at ";
15532  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
15533  }
15534  else if(ActionEventType == FailSplitDueToOtherTrain)
15535  {
15536  Prefix = " HELD: ";
15537  ErrorLog = " unable to split - other train obstructing at ";
15538  WarningStr = " unable to split - other train obstructing at ";
15539  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
15540  }
15541  else if(ActionEventType == FailUnexpectedBuffers)
15542  {
15543  ErrorLog = " stopped at buffers unexpectedly at position ";
15544  }
15545  else if(ActionEventType == FailMissedArrival)
15546  {
15547  ErrorLog = " failed to stop at ";
15548  MissedStops++;
15549  }
15550  else if(ActionEventType == FailMissedSplit)
15551  {
15552  ErrorLog = " failed to split at ";
15554  }
15555  else if(ActionEventType == FailMissedJBO)
15556  {
15557  ErrorLog = " failed to be joined by other train at ";
15559  }
15560  else if(ActionEventType == FailMissedJoinOther)
15561  {
15562  ErrorLog = " failed to join other train at ";
15564  }
15565  else if(ActionEventType == FailMissedTerminate)
15566  {
15567  ErrorLog = " failed to terminate at ";
15569  }
15570  else if(ActionEventType == FailMissedNewService)
15571  {
15572  ErrorLog = " failed to form new service at ";
15574  }
15575  else if(ActionEventType == FailMissedExitRailway)
15576  {
15577  ErrorLog = " failed to exit railway ";
15579  }
15580  else if(ActionEventType == FailMissedChangeDirection)
15581  {
15582  ErrorLog = " failed to change direction at ";
15583 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
15584  }
15585  else if(ActionEventType == FailMissedPass)
15586  {
15587  ErrorLog = " failed to pass ";
15588 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
15589  }
15590  else if(ActionEventType == FailBuffersPreventingStart)
15591  {
15592  ErrorLog = " facing buffers and unable to start at ";
15593  }
15594  else if(ActionEventType == FailDerailed)
15595  {
15596  ErrorLog = " DERAILED at position ";
15597  Prefix = " DERAILMENT: ";
15598  Derailments++;
15599  }
15600  else if(ActionEventType == FailBufferCrash)
15601  {
15602  ErrorLog = " CRASHED INTO BUFFERS at ";
15603  Prefix = " CRASH: ";
15604  CrashedTrains++;
15605  }
15606  else if(ActionEventType == FailLevelCrossingCrash)
15607  {
15608  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
15609  Prefix = " CRASH: ";
15610  CrashedTrains++;
15611  }
15612  else if(ActionEventType == FailCrashed)
15613  {
15614  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
15615  Prefix = " CRASH: ";
15616  CrashedTrains++;
15617  CrashedTrains++;
15618  }
15619  else if(ActionEventType == FailSPAD)
15620  {
15621  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
15622  Prefix = " SPAD: ";
15623  SPADEvents++;
15624  }
15625  else if(ActionEventType == FailLockedRoute)
15626  {
15627  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
15628  Prefix = " SPAD RISK: ";
15629  SPADRisks++;
15630  }
15631  else if(ActionEventType == RouteForceCancelled)
15632  {
15633  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
15634  }
15635  else if(ActionEventType == WaitingForJBO)
15636  {
15637  Prefix = " WARNING: ";
15638  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
15639  WarningStr = " waiting to join " + OtherHeadCode + " at ";
15640  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
15641  }
15642  else if(ActionEventType == WaitingForFJO)
15643  {
15644  Prefix = " WARNING: ";
15645  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
15646  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
15647  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
15648  }
15649 
15650  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
15651  PerfLogForm->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
15652  Utilities->CallLogPop(1371);
15653 }
15654 
15655 // ---------------------------------------------------------------------------
15656 
15658 {
15659 /* //for testing purposes
15660  TrainDataEntry
15661  AnsiString HeadCode, Description;//null on creation
15662  int StartSpeed, MaxRunningSpeed;//both kph
15663  int RepeatNumber;
15664  TActionVector ActionVector;
15665  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
15666  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
15667 
15668  ActionVectorEntry
15669  TTimetableEntryType FormatType;
15670  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
15671  AnsiString LocationName, Command, OtherHeadCode;//null on creation
15672  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
15673  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15674  int RepeatNumber;
15675 
15676  TrainOperatingData
15677  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
15678  int TrainID;
15679  TRunningEntry RunningEntry;
15680  TDateTime StartTime;
15681  AnsiString HeadCode;
15682 */
15683  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
15684  std::ofstream OutFile("TrainData.csv");
15685 
15686  if(OutFile == 0)
15687  {
15688  ShowMessage("Output file TrainData.csv failed to open");
15689  Utilities->CallLogPop(1372);
15690  return;
15691  }
15692  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15693  {
15694  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15695  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
15696 
15697  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
15698  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
15699 
15700  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
15701  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
15702  "RepeatNumber" << '\n' << '\n';
15703  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15704  {
15705  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15706  AnsiString TimetableEntryTypeStr;
15707  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
15708  switch(AVEntry.FormatType)
15709  {
15710  case 0:
15711  {
15712  TimetableEntryTypeStr = "NoFormat";
15713  break;
15714  }
15715 
15716  case 1:
15717  {
15718  TimetableEntryTypeStr = "TimeLoc";
15719  break;
15720  }
15721 
15722  case 2:
15723  {
15724  TimetableEntryTypeStr = "TimeTimeLoc";
15725  break;
15726  }
15727 
15728  case 3:
15729  {
15730  TimetableEntryTypeStr = "TimeCmd";
15731  break;
15732  }
15733 
15734  case 4:
15735  {
15736  TimetableEntryTypeStr = "StartNew";
15737  break;
15738  }
15739 
15740  case 5:
15741  {
15742  TimetableEntryTypeStr = "TimeCmdHeadCode";
15743  break;
15744  }
15745 
15746  case 6:
15747  {
15748  TimetableEntryTypeStr = "FinRemHere";
15749  break;
15750  }
15751 
15752  case 7:
15753  {
15754  TimetableEntryTypeStr = "FNSShuttle";
15755  break;
15756  }
15757 
15758  case 8:
15759  {
15760  TimetableEntryTypeStr = "SNTShuttle";
15761  break;
15762  }
15763 
15764  case 9:
15765  {
15766  TimetableEntryTypeStr = "SNSShuttle";
15767  break;
15768  }
15769 
15770  case 10:
15771  {
15772  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
15773  break;
15774  }
15775 
15776  case 11:
15777  {
15778  TimetableEntryTypeStr = "FSHNewService";
15779  break;
15780  }
15781 
15782  case 12:
15783  {
15784  TimetableEntryTypeStr = "Repeat";
15785  break;
15786  }
15787 
15788  default:
15789  {
15790  TimetableEntryTypeStr = "Default";
15791  break;
15792  }
15793  }
15794  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
15795  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
15796  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
15797  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
15798  AVEntry.NumberOfRepeats << '\n';
15799  }
15800  OutFile << '\n';
15801  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
15802  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
15803  {
15804  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
15805  AnsiString RunningEntryStr;
15806  // NotStarted, Running, Exited
15807  switch(TOD.RunningEntry)
15808  {
15809  case 0:
15810  {
15811  RunningEntryStr = "NotStarted";
15812  break;
15813  }
15814 
15815  case 1:
15816  {
15817  RunningEntryStr = "Running";
15818  break;
15819  }
15820 
15821  case 2:
15822  {
15823  RunningEntryStr = "Exited";
15824  break;
15825  }
15826  }
15827  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
15828  }
15829  OutFile << '\n';
15830  }
15831  OutFile.close();
15832  Utilities->CallLogPop(1373);
15833 }
15834 
15835 // ---------------------------------------------------------------------------
15836 
15837 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
15838 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
15839 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
15840 {
15841  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
15842  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15844  ShowMessage(Message);
15845  BaseTime = TDateTime::CurrentDateTime();
15846  StopTTClockFlag = false;
15847  Utilities->CallLogPop(1374);
15848 }
15849 
15850 // ---------------------------------------------------------------------------
15851 
15852 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
15853 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
15854 // from the start of the relevant vectors. Can't save the pointer values
15855 // as these will be different each time the vectors are created
15856 {
15857  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
15858  Utilities->SaveFileInt(SessionFile, TrainVector.size());
15859  for(unsigned int x = 0; x < TrainVector.size(); x++)
15860  {
15861  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
15862  }
15863  Utilities->CallLogPop(1375);
15864 }
15865 
15866 // ---------------------------------------------------------------------------
15867 
15868 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
15869 {
15870  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
15871  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
15872  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15873  // by zero error in calculating AValue, use 1
15874  for(int x = 0; x < NumberOfTrains; x++)
15875  {
15876  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15877  // by zero error in calculating AValue, use 1
15878  NewTrain->LoadOneSessionTrain(0, SessionFile);
15879  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
15880  // added at v2.4.0. have to include as that value not stored in session file
15881  {
15882  NewTrain->StoppedWithoutPower = true;
15883  }
15884  TrainVector.push_back(*NewTrain);
15885  LastTrainLoaded = x;
15886  }
15887  delete NewTrain;
15888  Utilities->CallLogPop(1376);
15889 }
15890 
15891 // ---------------------------------------------------------------------------
15892 
15893 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
15894 {
15895  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
15896  int NumberOfTrains;
15897 
15898  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
15899  {
15900  Utilities->CallLogPop(1377);
15901  return(false);
15902  }
15903  for(int x = 0; x < NumberOfTrains; x++)
15904  {
15905  if(!(TTrain::CheckOneSessionTrain(InFile)))
15906  {
15907  Utilities->CallLogPop(1378);
15908  return(false);
15909  }
15910  }
15911  Utilities->CallLogPop(1379);
15912  return(true);
15913 }
15914 
15915 // ---------------------------------------------------------------------------
15916 
15917 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
15918 {
15919  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
15920  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
15921  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
15922  {
15923  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
15924  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
15925  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
15926  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
15927  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
15928  }
15929  Utilities->CallLogPop(1380);
15930 }
15931 
15932 // ---------------------------------------------------------------------------
15933 
15934 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15935 {
15936  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
15937  TAllRoutes::TLockedRouteClass LockedRouteObject;
15938  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
15939 
15940  for(int x = 0; x < LockedRouteVectorSize; x++)
15941  {
15942  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15943  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15944  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15945  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15946  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15947  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15948  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15949  }
15950  Utilities->CallLogPop(1381);
15951 }
15952 
15953 // ---------------------------------------------------------------------------
15954 
15955 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15956 {
15957  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15958  int LockedRouteVectorSize;
15959 
15960  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15961  {
15962  Utilities->CallLogPop(1382);
15963  return(false);
15964  }
15965  for(int x = 0; x < LockedRouteVectorSize; x++)
15966  {
15967  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15968  {
15969  Utilities->CallLogPop(1383);
15970  return(false);
15971  }
15972  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15973  {
15974  Utilities->CallLogPop(1384);
15975  return(false);
15976  }
15977  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15978  {
15979  Utilities->CallLogPop(1385);
15980  return(false);
15981  }
15982  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15983  {
15984  Utilities->CallLogPop(1386);
15985  return(false);
15986  }
15987  if(!Utilities->CheckFileDouble(SessionFile))
15988  {
15989  Utilities->CallLogPop(1387);
15990  return(false);
15991  }
15992  }
15993  Utilities->CallLogPop(1388);
15994  return(true);
15995 }
15996 
15997 // ---------------------------------------------------------------------------
15998 
15999 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
16000 {
16001  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
16002  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
16003  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
16004  {
16005  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
16006  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
16007  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
16008  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
16009  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
16010  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
16011  }
16012  Utilities->CallLogPop(1389);
16013 }
16014 
16015 // ---------------------------------------------------------------------------
16016 
16017 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
16018 {
16019  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
16020  TContinuationAutoSigEntry ContinuationAutoSigObject;
16021  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
16022 
16023  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
16024  {
16025  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
16026  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
16027  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
16028  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
16029  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
16030  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
16031  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
16032  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
16033  }
16034  Utilities->CallLogPop(1390);
16035 }
16036 
16037 // ---------------------------------------------------------------------------
16038 
16039 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
16040 {
16041  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
16042  int ContinuationAutoSigVectorSize;
16043 
16044  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
16045  {
16046  Utilities->CallLogPop(1391);
16047  return(false);
16048  }
16049  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
16050  {
16051  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16052  {
16053  Utilities->CallLogPop(1392);
16054  return(false);
16055  }
16056  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
16057  {
16058  Utilities->CallLogPop(1393);
16059  return(false);
16060  }
16061  if(!Utilities->CheckFileDouble(SessionFile))
16062  {
16063  Utilities->CallLogPop(1405);
16064  return(false);
16065  }
16066  if(!Utilities->CheckFileDouble(SessionFile))
16067  {
16068  Utilities->CallLogPop(1406);
16069  return(false);
16070  }
16071  if(!Utilities->CheckFileDouble(SessionFile))
16072  {
16073  Utilities->CallLogPop(1407);
16074  return(false);
16075  }
16076  if(!Utilities->CheckFileDouble(SessionFile))
16077  {
16078  Utilities->CallLogPop(1394);
16079  return(false);
16080  }
16081  }
16082  Utilities->CallLogPop(1395);
16083  return(true);
16084 }
16085 
16086 // ---------------------------------------------------------------------------
16087 
16088 /*
16089  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
16090  {
16091  public:
16092  AnsiString Description; ///< service description
16093  AnsiString HeadCode; ///< service headcode
16094  int RepeatNumber; ///< service RepeatNumber
16095  int IncrementalMinutes; ///< Repeat separation in minutes
16096  int IncrementalDigits; ///< Repeat headcode separation
16097  int VectorPosition; ///< TrackVectorPosition for the continuation element
16098  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
16099  };
16100 
16101 
16102  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
16103  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
16104 */
16105 
16107 // build this into timetable load so session loading can use it too
16108 // being a multimap it automatically sorts in ascending EventTime order
16109 {
16110  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
16112  // need to clear as this called twice when load a session
16113  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16114  {
16115  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
16116  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
16117  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
16118 
16119  if(AVFirstEntry.Command == "Snt")
16120  // new train (no need to include Snt-sh since they can't start at a continuation)
16121  {
16124  {
16126  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
16127  // retains this value for all repeats
16128  CTEEntry.RepeatNumber = 0; // for first entry
16129  CTEEntry.TrainDataEntryPtr = &TDEntry;
16130  // retains this value for all repeats
16131  CTEEntry.HeadCode = TDEntry.HeadCode;
16132  CTEEntry.Description = TDEntry.Description;
16133  CTEEntry.IncrementalMinutes = 0;
16134  CTEEntry.IncrementalDigits = 0;
16135  if(AVLastEntry.FormatType == Repeat)
16136  {
16137  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
16138  // retains this value or 0 for all repeats
16139  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
16140  // retains this value or 0 for all repeats
16141  }
16142  CTEMMP.first = AVFirstEntry.EventTime;
16143  CTEMMP.second = CTEEntry;
16144  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
16145  // base entry
16146  if(TDEntry.NumberOfTrains > 1)
16147  {
16148  if(AVLastEntry.FormatType != Repeat)
16149  {
16150  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
16151  }
16152  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
16153  {
16154  CTEEntry.RepeatNumber = y;
16155  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
16156  // CTEEntry.VectorPosition stays same
16157  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
16158  CTEMMP.second = CTEEntry;
16159  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
16160  }
16161  }
16162  }
16163  }
16164  }
16165  Utilities->CallLogPop(1396);
16166 }
16167 
16168 // ---------------------------------------------------------------------------
16169 
16171 {
16172  // called when WarningFlashCount == 0 or when press zoomout button
16173  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
16174  if(!Display->ZoomOutFlag)
16175  {
16176  Utilities->CallLogPop(1156);
16177  return;
16178  }
16179  for(unsigned int x = 0; x < TrainVector.size(); x++)
16180  {
16181  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
16182  // if OldPlotElement[x] == -1 then ignore (not plotted)
16184  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
16185  }
16186  Display->Update();
16187  // need to keep this since Update() not called for PlotSmallOutput as too slow
16188  Utilities->CallLogPop(742);
16189 }
16190 
16191 // ---------------------------------------------------------------------------
16192 
16193 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
16194 {
16195  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
16196  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
16197  {
16198  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
16199  }
16200  Utilities->CallLogPop(740);
16201  return(TrainVector.at(VecPos));
16202 }
16203 
16204 // ---------------------------------------------------------------------------
16205 
16206 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
16207 {
16208  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
16209  AnsiString RetStr = "", PartStr = "";
16210 
16211 
16212 /*
16213  Have description & mass etc for train at top - header, then array of actions
16214 
16215  class TActionVectorEntry
16216  {
16217  public:
16218  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
16220  bool SignallerControl;
16222  bool Warning;
16224  int NumberOfRepeats;
16226  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
16228  TDateTime EventTime, ArrivalTime, DepartureTime;
16230  TNumList ExitList;
16232  TTimetableFormatType FormatType;
16234  TTimetableLocationType LocationType;
16236  TTimetableSequenceType SequenceType;
16238  TTimetableShuttleLinkType ShuttleLinkType;
16240  TTrainDataEntry *LinkedTrainEntryPtr;
16242  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
16244 
16245  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
16246 
16247  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
16248 
16249  class TTrainOperatingData
16250  {
16251  public:
16252  int TrainID;
16253  TActionEventType EventReported;
16254  TRunningEntry RunningEntry;
16255 
16256  //inline function
16257  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
16258  };
16259 
16260  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
16261 
16262  class TTrainDataEntry
16263  {
16264  public:
16265  AnsiString HeadCode, ServiceReference, Description;
16267  double MaxBrakeRate;
16269  double MaxRunningSpeed;
16271  double PowerAtRail;
16273  int Mass;
16275  int NumberOfTrains;
16277  int SignallerSpeed;
16279  int StartSpeed;
16281  TActionVector ActionVector;
16283  TTrainOperatingDataVector TrainOperatingDataVector;
16285 
16286  //inline function
16287  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
16288  };
16289 
16290  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
16291 
16292  //formatted timetable types
16293  class TOneTrainFormattedEntry
16294  {
16295  AnsiString Action;//includes location if relevanr
16296  AnsiString Time;
16297  };
16298 
16299  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16300 
16301  class TOneCompleteFormattedTrain//headcode + list of actions
16302  {
16303  public:
16304  AnsiString HeadCode;
16305  TOneFormattedTrainVector OneFormattedTrainVector;
16306  };
16307 
16308  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16309 
16310  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16311  {
16312  public:
16313  AnsiString Header;//description, mass, power, brake rate etc
16314  int NumberOfTrains;// number of repeats + 1
16315  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16316  };
16317 
16318 
16319  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16320  //end of formatted timetable types
16321 
16322 */
16323 
16324  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16325 
16326  // format "16/06/2009 20:55:17"
16327  // avoid characters in filename:= / \ : * ? " < > |
16328  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16329 
16330  AnsiString ShortTTName = "";
16331 
16332  for(int x = TTFileName.Length(); x > 0; x--)
16333  {
16334  if(TTFileName[x] == '\\')
16335  {
16336  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
16337  break;
16338  }
16339  }
16340 
16341  ShowMessage("Creates two timetables named " + ShortTTName +
16342  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
16343 
16344  Screen->Cursor = TCursor(-11); // Hourglass
16345 
16346  AnsiString FormatNoDPStr = "#######0";
16347  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
16348 
16350  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
16351  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
16352 
16353  // all timetable in formatted form
16354  //create the AllTTTrains vector
16355  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16356  {
16357  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
16358  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
16359  if(TrainDataEntry.Mass > 0)
16360  {
16361  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
16362  }
16363  if(TrainDataEntry.PowerAtRail > 0)
16364  {
16365  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
16366  }
16367  if(TrainDataEntry.MaxBrakeRate > 0)
16368  {
16369  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
16370  }
16371  if(TrainDataEntry.MaxRunningSpeed > 0)
16372  {
16373  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
16374  }
16375  FirstHeadCode = TrainDataEntry.HeadCode;
16376  int IncDigits = 0, IncMinutes = 0;
16377  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16378  if(!ActionVector.empty())
16379  {
16380  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16381  {
16382  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
16383  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16384  }
16385  }
16386  TTrainFormattedInformation OneTTLine;
16387  // contains all information for a single TT entry (including repeats)
16388  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
16389  {
16390  OneTTLine.Header = "";
16391  if((TrainDataEntry.Description != "") && (MassStr != ""))
16392  {
16393  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
16394  }
16395  else if(TrainDataEntry.Description != "")
16396  {
16397  OneTTLine.Header = TrainDataEntry.Description;
16398  }
16399  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
16400  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
16401  for(unsigned int z = 0; z < ActionVector.size(); z++)
16402  {
16403  TOneTrainFormattedEntry OneTTEntry;
16404  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
16405  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
16406  AnsiString PartStr = "", TimeStr = "";
16407 /*
16408  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
16409  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
16410  ExitRailway};
16411  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
16412  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
16413  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
16414 */
16415  if(ActionVectorEntry.SequenceType == Start)
16416  {
16417  if(ActionVectorEntry.FormatType == StartNew)
16418  {
16419  if(ActionVectorEntry.LocationName != "")
16420  {
16421  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
16422  {
16423  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16424  }
16425  else
16426  {
16427  PartStr = "Created at " + ActionVectorEntry.LocationName;
16428  }
16429  }
16430  else // may be a named continuation or other element, and if so report that
16431  {
16432  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
16433  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
16434  {
16435  if(LocName != "")
16436  {
16437  PartStr = "Enters at " + LocName;
16438  }
16439  else // use rear position if it's a continuation
16440  {
16441  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16442  }
16443  }
16444  else // not a continuation
16445  {
16446  if(LocName != "")
16447  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
16448  // but include anyway
16449  {
16450  PartStr = "Created at " + LocName;
16451  }
16452  else // use rear position again
16453  {
16454  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16455  }
16456  }
16457  }
16458  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
16459  }
16460  else if(ActionVectorEntry.FormatType == SNTShuttle)
16461  {
16462  if(y == 0) // first train
16463  {
16464  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16465  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
16466  }
16467  else
16468  {
16469  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16470  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16471  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
16472  } // y-1 for headcode above since it is the last repeat value that the train is from
16473 
16474  }
16475  else if(ActionVectorEntry.Command == "Sfs")
16476  {
16477  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
16478  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16479  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
16480  }
16481  else if(ActionVectorEntry.Command == "Sns")
16482  {
16483  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16484  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16485  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
16486  }
16487  else if(ActionVectorEntry.FormatType == SNSShuttle)
16488  {
16489  if(y == 0) // first entry from shuttle
16490  {
16491  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16492  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
16493  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
16494  }
16495  else
16496  {
16497  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16498  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16499  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
16500  } // y-1 for headcode above since it is the last repeat value that the train is from
16501 
16502  }
16503  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
16504  {
16505  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16506  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
16507  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
16508  AnsiString FirstHeadCode = TDE->HeadCode;
16509  int LastRepeatNumber = TDE->NumberOfTrains - 1;
16510  // a shuttle has to have at least 1 repeat
16511  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
16512  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
16513  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
16514  }
16515  }
16516  else if(ActionVectorEntry.SequenceType == Intermediate)
16517  {
16518  if(ActionVectorEntry.FormatType == TimeTimeLoc)
16519  {
16520  // here need 2 entries if times different so push the first right away & the second later
16521  // if times same just give the arrival entry
16522  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
16523  {
16524  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16525  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16526  OneTTEntry.Action = PartStr;
16527  OneTTEntry.Time = TimeStr;
16528  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16529  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16530  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
16531  }
16532  else
16533  {
16534  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
16535  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16536  }
16537  }
16538  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
16539  {
16540  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16541  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16542  }
16543  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
16544  {
16545  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16546  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
16547  }
16548  else if(ActionVectorEntry.FormatType == PassTime)
16549  {
16550  PartStr = "Passes " + ActionVectorEntry.LocationName;
16551  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
16552  }
16553  else if(ActionVectorEntry.Command == "jbo")
16554  {
16555  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
16556  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16557  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
16558  }
16559  else if(ActionVectorEntry.Command == "fsp")
16560  {
16561  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
16562  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16563  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
16564  }
16565  else if(ActionVectorEntry.Command == "rsp")
16566  {
16567  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
16568  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16569  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
16570  }
16571  else if(ActionVectorEntry.Command == "cdt")
16572  {
16573  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
16574  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
16575  }
16576  }
16577  else if(ActionVectorEntry.SequenceType == Finish)
16578  {
16579  if(ActionVectorEntry.Command == "Fns")
16580  {
16581  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16582  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16583  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
16584  }
16585  else if(ActionVectorEntry.Command == "F-nshs")
16586  {
16587  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16588  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16589  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
16590  }
16591  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16592  {
16593  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
16594  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16595  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
16596  // y+1 because it's the NEXT service repeat number that is relevant
16597  }
16598  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16599  {
16600  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16601  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16602  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
16603  }
16604  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16605  {
16606  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16607  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16608  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
16609  // y+1 because it's the NEXT service repeat number that is relevant
16610  }
16611  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16612  {
16613  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
16614  // only used in chronological tt
16615  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
16616  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
16617  }
16618  else if(ActionVectorEntry.Command == "Frh")
16619  {
16620  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
16621  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
16622  if(z > 0)
16623  // should be for finish entry but include check for safety
16624  {
16625  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16626  {
16627  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16628  }
16629  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16630  {
16631  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16632  }
16633  else
16634  {
16635  TimeStr = " "; // shouldn't ever get here
16636  }
16637  }
16638  }
16639  else if(ActionVectorEntry.Command == "Fer")
16640  {
16641  AnsiString AllowedExits;
16642  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
16643  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
16644  }
16645  else if(ActionVectorEntry.Command == "Fjo")
16646  {
16647  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
16648  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16649  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
16650  }
16651  }
16652  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
16653  {
16654  continue; // no entry needed for a repeat
16655  }
16656  OneTTEntry.Action = PartStr;
16657  OneTTEntry.Time = TimeStr;
16658  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16659  // one per action
16660  }
16661  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
16662  // one per repeat
16663  }
16664  AllTTTrains->push_back(OneTTLine); // one per repeating train
16665  }
16666  // AllTTTrains vector now complete
16667 
16668  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
16669 
16670  if(TTFile == 0)
16671  {
16672  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
16673  delete AllTTTrains;
16674  Utilities->CallLogPop(1567);
16675  return;
16676  }
16677 /* formatted timetable types
16678  class TOneTrainFormattedEntry
16679  {
16680  AnsiString Action;//includes location if relevant
16681  AnsiString Time;
16682  };
16683 
16684  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16685 
16686  class TOneCompleteFormattedTrain//headcode + list of actions
16687  {
16688  public:
16689  AnsiString HeadCode;
16690  TOneFormattedTrainVector OneFormattedTrainVector;
16691  };
16692 
16693  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16694 
16695  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16696  {
16697  public:
16698  AnsiString Header;//description, mass, power, brake rate etc
16699  int NumberOfTrains;// number of repeats + 1
16700  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16701  };
16702 
16703  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16704  //end of formatted timetable types
16705 */
16706 
16707  // new layout using multiple rows
16708  TTFile << TableTitle.c_str() << '\n' << '\n';
16709  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16710  {
16711  TTFile << AllTTTrains->at(x).Header.c_str();
16712  TTFile << '\n';
16713  TTFile << ','; // for the blank line above the action list
16714  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16715  {
16716  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16717  {
16718  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
16719  }
16720  else
16721  {
16722  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
16723  }
16724  }
16725  TTFile << '\n' << '\n';
16726 
16727  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
16728  {
16729  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
16730  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16731  {
16732  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16733  {
16734  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
16735  }
16736  else
16737  {
16738  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
16739  }
16740  }
16741  TTFile << '\n';
16742  }
16743  TTFile << '\n' << '\n';
16744  }
16745 
16746  TTFile.close();
16747 
16748  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16749 
16750  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16751 
16752  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
16753 
16754  if(TTFile2 == 0)
16755  {
16756  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
16757  delete AllTTTrains;
16758  Utilities->CallLogPop(1710);
16759  return;
16760  }
16761  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
16762  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
16763  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
16764 
16765  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
16766  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
16767 
16768  // multimap of AnsiStrings with TimeString as key (to sort automatically)
16769 
16770  TTFile2 << TableTitle.c_str() << '\n' << '\n';
16771  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16772  {
16773  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16774  {
16775  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
16776  {
16777  bool GiveMessagesFalse = false;
16778  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
16779  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
16780  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
16781  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
16782  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
16783  {
16784  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
16785  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
16786  TimeString = TimeString.SubString(9, 5);
16787  ActionString += " " + OtherHeadCode;
16788  }
16789  if(TimeString.SubString(1, 7) == "End at ")
16790  // for Frh-sh final entry
16791  {
16792  TimeString = TimeString.SubString(8, 5);
16793  }
16794  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
16795  AnsiMultiMapEntry.first = TimeString;
16796  AnsiMultiMapEntry.second = OneLine;
16797  TAMM->insert(AnsiMultiMapEntry);
16798  }
16799  }
16800  }
16801 
16802  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
16803  {
16804  TTFile2 << (AMMIT->second).c_str();
16805  }
16806  delete AllTTTrains;
16807  delete TAMM;
16808  TTFile2.close();
16809  Utilities->CallLogPop(1580);
16810 }
16811 
16812 // ---------------------------------------------------------------------------
16813 
16814 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
16815  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
16816 {
16817 
16818  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
16819  bool AnalysisError = false;
16820  AnsiString SequenceLog = "SequenceLog\n";
16821 
16822 /* Double crosslink (shuttle) table:
16823 
16824 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16825  Code ShuttleLink- EntryPtr ShuttleLink-
16826  HeadCode EntryPtr
16827 
16828 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16829 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16830 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16831 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16832 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16833 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16834 
16835 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16836 */
16837 
16838  try
16839  {
16840  //New section at v2.5.0 for tt conflict analysis
16841  /*
16842  typedef std::list<AnsiString> TServiceCallingLocsList;
16843  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
16844 
16846  struct TLocServiceTimes
16847  {
16848  AnsiString Location;
16849  AnsiString ServiceAndRepeatNum;
16850  AnsiString AtLocTime;
16851  AnsiString ArrTime;
16852  AnsiString DepTime;
16853  AnsiString FrhMarker;
16854  };
16855  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16856  */
16857 
16858  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
16859  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
16860 
16861  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
16862  TTrainDataVector::iterator TDVIt, TDVCopyIt;
16863  int Suffix = 0;
16864  int IteratorNumber = 0;
16865  AnsiString AnsiSuffix = "";
16866  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
16867  {
16868  IteratorNumber++; //first value in loop is 1
16869  Suffix = 0;
16870  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
16871  {
16872  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
16873  {
16874  Suffix++; //first value is 1
16875  AnsiSuffix = AnsiString(Suffix);
16876  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
16877  }
16878  }
16879  }
16880  SequenceLog += "1\n";
16881  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
16882  TServiceCallingLocsList ServiceCallingLocsList;
16883  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
16884  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16885  {
16886  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16887  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16888  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
16889  ServiceCallingLocsList.clear();
16890  if(ActionVector.empty())
16891  {
16892  continue;
16893  }
16894  if(ActionVector.at(0).SignallerControl)
16895  {
16896  continue;
16897  }
16898  for(unsigned int z = 0; z < ActionVector.size(); z++)
16899  {
16900  TActionVectorEntry AVE = ActionVector.at(z);
16901  if(AVE.FormatType == StartNew)
16902  {
16903  if(AVE.LocationType == AtLocation) //located Snt
16904  {
16905  ServiceCallingLocsList.push_back(AVE.LocationName);
16906  }
16907  else //unlocated Snt (could be entering at continuation)
16908  {
16910  if(TE.ActiveTrackElementName != "")
16911  {
16912  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
16913  }
16914  else
16915  {
16916  int HLoc = TE.HLoc;
16917  int VLoc = TE.VLoc;
16918  AnsiString HString;
16919  AnsiString VString;
16920  if(HLoc < 0)
16921  {
16922  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16923  }
16924  else
16925  {
16926  HString = AnsiString(HLoc);
16927  }
16928  if(VLoc < 0)
16929  {
16930  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16931  }
16932  else
16933  {
16934  VString = AnsiString(VLoc);
16935  }
16936  ServiceCallingLocsList.push_back(HString + '-' + VString);
16937  }
16938  }
16939  }
16940  else if(AVE.SequenceType == Start) //other start entries, all located
16941  {
16942  ServiceCallingLocsList.push_back(AVE.LocationName);
16943  }
16944  else if(AVE.FormatType == TimeLoc) //z must be > 0
16945  {
16946  if(ServiceCallingLocsList.back() != AVE.LocationName)
16947  {
16948  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
16949  }
16950  }
16951  else if(AVE.FormatType == PassTime)
16952  {
16953  ServiceCallingLocsList.push_back(AVE.LocationName);
16954  }
16955  else if(AVE.FormatType == TimeTimeLoc)
16956  {
16957  ServiceCallingLocsList.push_back(AVE.LocationName);
16958  }
16959  else if(AVE.Command == "cdt") //list if not next to start or finish
16960  {
16961  if(ActionVector.at(z-1).SequenceType == Start)
16962  {
16963  continue;
16964  }
16965  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16966  {
16967  continue;
16968  }
16969  else
16970  {
16971  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16972  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16973  }
16974  }
16975  else if(AVE.FormatType == ExitRailway) //Fer
16976  {
16977  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16978  AnsiString LName = TE.ActiveTrackElementName;
16979  if(LName != "")
16980  {
16981  ServiceCallingLocsList.push_back(LName);
16982  }
16983  else
16984  {
16985  int HLoc = TE.HLoc;
16986  int VLoc = TE.VLoc;
16987  AnsiString HString;
16988  AnsiString VString;
16989  if(HLoc < 0)
16990  {
16991  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16992  }
16993  else
16994  {
16995  HString = AnsiString(HLoc);
16996  }
16997  if(VLoc < 0)
16998  {
16999  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17000  }
17001  else
17002  {
17003  VString = AnsiString(VLoc);
17004  }
17005  ServiceCallingLocsList.push_back(HString + '-' + VString);
17006  }
17007  }
17008  }
17009  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
17010  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
17011  }
17012  //AllServiceCallingLocsMap built
17013  SequenceLog += "2\n";
17014 /* this sequence is to test the validity of AllServiceCallingLocsMap
17015  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17016  std::ofstream Test(TestFile.c_str());
17017 
17018  if(TestFile == 0)
17019  {
17020  ShowMessage("TestFile failed to open - can't be created");
17021  Utilities->CallLogPop();
17022  return false;
17023  }
17024 
17025  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
17026  {
17027  Test << ASCLIt->first << '\n'; //service ref
17028  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
17029  {
17030  Test << *SCLIt << '\n';
17031  }
17032  Test << "\n\n";
17033  }
17034  Test.close();
17035  Utilities->CallLogPop();
17036  return true;
17037 */
17038 
17039  //initialise variables before calc LastTTTime & build LocServiceTimesVector
17040  if(TrainDataVector.empty())
17041  {
17042  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
17043  Utilities->CallLogPop(2209);
17044  return(false);
17045  }
17046  TLocServiceTimes TLSTEntry;
17047  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
17048  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
17049  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
17050  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
17051  LastTTTime = "";
17052  SequenceLog += "3\n";
17053  //calculate LastTTTime
17054  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17055  {
17056  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17057  TActionVector &ActionVector = TrainDataEntry.ActionVector;
17058  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
17059  TDateTime LastTDTime;
17060  int IncMinutes = 0;
17061  NumTrains = TrainDataEntry.NumberOfTrains;
17062  if(ActionVector.empty())
17063  {
17064  continue;
17065  }
17066  if(ActionVector.at(0).SignallerControl)
17067  {
17068  continue;
17069  }
17070  if(AVLast->FormatType == Repeat)
17071  {
17072  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17073  AVLast--; //now points to the command before the repeat
17074  }
17075  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
17076  {
17077  AVLast--; //points to last timed entry
17078  }
17079  //here AVLast points to last entry with a time
17080  if(AVLast->ArrivalTime != TDateTime(-1))
17081  {
17082  LastTDTime = AVLast->ArrivalTime;
17083  }
17084  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
17085  {
17086  LastTDTime = AVLast->EventTime;
17087  }
17088  else
17089  {
17090  continue; //shouldn't ever reach here but if do then skip this service
17091  }
17092  if(NumTrains == 1)
17093  {
17094  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
17095  }
17096  else
17097  {
17098  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
17099  }
17100  if(LastAnsiTime > LastTTTime)
17101  {
17102  LastTTTime = LastAnsiTime;
17103  }
17104  }
17105  SequenceLog += "4\n";
17106 //build LocServiceTimesVector
17107 
17108 /*
17109  struct TLocServiceTimes
17110  {
17111  AnsiString Location;
17112  AnsiString ServiceAndRepeatNum;
17113  AnsiString AtLocTime;
17114  AnsiString ArrTime;
17115  AnsiString DepTime;
17116  AnsiString FrhMarker;
17117  };
17118  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17119 
17120 This works as follows:
17121 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
17122 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
17123 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
17124 
17125 Every action for every train is examined and times entered as follows:-
17126 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
17127 b) an unlocated Snt: entry time becomes DepTime
17128 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
17129 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
17130 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
17131 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
17132 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
17133 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
17134 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
17135 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
17136 */
17137  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17138  {
17139  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17140  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17141  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
17142  int IncMinutes = 0;
17143  NumTrains = TrainDataEntry.NumberOfTrains;
17144  if(ActionVector.empty())
17145  {
17146  continue;
17147  }
17148  if(ActionVector.at(0).SignallerControl)
17149  {
17150  continue;
17151  }
17152  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
17153  {
17154  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17155  }
17156  for(int y = 0; y < NumTrains; y++) //y is the repeat number
17157  {
17158  if(NumTrains == 1)
17159  {
17160  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
17161  }
17162  else if(y == 0)
17163  {
17164  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
17165  }
17166  else
17167  {
17168  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
17169  }
17170  for(unsigned int z = 0; z < ActionVector.size(); z++)
17171  {
17172  TActionVectorEntry AVE = ActionVector.at(z);
17173  TLSTEntry.AtLocTime = "";
17174  TLSTEntry.ArrTime = "";
17175  TLSTEntry.DepTime = "";
17176  TLSTEntry.Location = "";
17177  TLSTEntry.FrhMarker = "";
17178 
17179  if(AVE.FormatType == StartNew) //Snt only
17180  {
17181  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
17182  {
17183  TLSTEntry.Location = AVE.LocationName;
17184  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
17185  LocServiceTimesVector.push_back(TLSTEntry);
17186 
17187  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17188  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
17189  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17190  {
17191  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17192  {
17193  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
17194  break;
17195  }
17196  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
17197  {
17198  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
17199  break;
17200  }
17201  }
17202  if(FoundStopTime == "")
17203  {
17204  throw Exception("Failure to determine FoundStopTime for located Snt");
17205  }
17206  int WhileCount = 0;
17207  while(true)
17208  {
17209  //add minutes until reach FoundStopTime but don't add that time
17210  WhileCount++;
17211  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17212  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17213  TLSTEntry.DepTime = "";
17214  TLSTEntry.ArrTime = "";
17215  if(IncTime >= FoundStopTime) //don't add that time
17216  {
17217  break;
17218  }
17219  LocServiceTimesVector.push_back(TLSTEntry);
17220  if(WhileCount > 2000)
17221  {
17222  throw Exception("While loop failed to break in 2000 loops for located Snt");
17223  }
17224  }
17225  }
17226  else //unlocated Snt, use the EventTime as DepTime for this vector
17227  {
17229  if(TE.ActiveTrackElementName != "")
17230  {
17231  TLSTEntry.Location = TE.ActiveTrackElementName;
17232  }
17233  else
17234  {
17235  int HLoc = TE.HLoc;
17236  int VLoc = TE.VLoc;
17237  AnsiString HString;
17238  AnsiString VString;
17239  if(HLoc < 0)
17240  {
17241  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17242  }
17243  else
17244  {
17245  HString = AnsiString(HLoc);
17246  }
17247  if(VLoc < 0)
17248  {
17249  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17250  }
17251  else
17252  {
17253  VString = AnsiString(VLoc);
17254  }
17255  TLSTEntry.Location = HString + '-' + VString;
17256  }
17257  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
17258  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
17259  LocServiceTimesVector.push_back(TLSTEntry);
17260  }
17261  }
17262 
17263  else if(AVE.SequenceType == Start) //other start entries, all located
17264  {
17265  TLSTEntry.Location = AVE.LocationName;
17266  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
17267  LocServiceTimesVector.push_back(TLSTEntry);
17268  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17269  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
17270  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17271  {
17272  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17273  {
17274  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
17275  break;
17276  }
17277  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
17278  {
17279  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
17280  break;
17281  }
17282  }
17283  if(FoundStopTime == "")
17284  {
17285  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
17286  }
17287  int WhileCount = 0;
17288  while(true)
17289  {
17290  //add minutes until reach FoundStopTime but don't add that time
17291  WhileCount++;
17292  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17293  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17294  TLSTEntry.DepTime = "";
17295  TLSTEntry.ArrTime = "";
17296  if(IncTime >= FoundStopTime) //don't add that time
17297  {
17298  break;
17299  }
17300  LocServiceTimesVector.push_back(TLSTEntry);
17301  if(WhileCount > 2000)
17302  {
17303  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
17304  }
17305  }
17306  }
17307 
17308  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
17309  {
17310  TLSTEntry.Location = AVE.LocationName;
17311  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
17312  {
17313  bool SkipAddingMinutes = false;
17314  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
17315  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
17316  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
17317  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17318  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
17319  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17320  {
17321  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17322  {
17323  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
17324  break;
17325  }
17326  if(ActionVector.at(a).SequenceType == Finish) //finish catered for in a later test
17327  {
17328  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
17329  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
17330  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
17331  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
17332  {
17333  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
17334  SkipAddingMinutes = true;
17335  }
17336  break;
17337  }
17338  }
17339  if(FoundStopTime == "")
17340  {
17341  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
17342  }
17343  if(!SkipAddingMinutes)
17344  {
17345  int WhileCount = 0;
17346  while(true)
17347  {
17348  //add minutes until reach FoundStopTime but don't add that time
17349  WhileCount++;
17350  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17351  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17352  TLSTEntry.DepTime = "";
17353  TLSTEntry.ArrTime = "";
17354  if(IncTime >= FoundStopTime) //don't add that time
17355  {
17356  break;
17357  }
17358  LocServiceTimesVector.push_back(TLSTEntry);
17359  if(WhileCount > 2000)
17360  {
17361  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
17362  }
17363  }
17364  }
17365  }
17366  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
17367  {
17368  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
17369  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
17370  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
17371  {
17372  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
17373  {
17374  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
17375  LocServiceTimesVector.pop_back();
17376  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
17377  }
17378  else //just add the dep & atloc times
17379  {
17380  TLSTEntry.ArrTime = "";
17381  LocServiceTimesVector.push_back(TLSTEntry);
17382  }
17383  }
17384  else //just add the dep & atloc times
17385  {
17386  TLSTEntry.ArrTime = "";
17387  LocServiceTimesVector.push_back(TLSTEntry);
17388  }
17389  }
17390  }
17391 
17392  else if(AVE.FormatType == TimeTimeLoc)
17393  {
17394  TLSTEntry.Location = AVE.LocationName;
17395  if(AVE.ArrivalTime > TDateTime(-1)) //should be
17396  {
17397  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
17398  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
17399  }
17400  if(AVE.DepartureTime > TDateTime(-1)) //should be
17401  {
17402  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
17403  }
17404  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
17405  {
17406  LocServiceTimesVector.push_back(TLSTEntry);
17407  }
17408  else
17409  {
17410  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
17411  TLSTEntry.DepTime = "";
17412  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
17413  TLSTEntry.ArrTime = ""; //done with this now
17414  while(TLSTEntry.AtLocTime < TempDepTime)
17415  {
17416  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17417  if(TLSTEntry.AtLocTime == TempDepTime)
17418  {
17419  TLSTEntry.DepTime = TempDepTime; //restore value
17420  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
17421  }
17422  else
17423  {
17424  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
17425  }
17426  }
17427  }
17428  }
17429 
17430  else if(AVE.FormatType == PassTime) //added at v2.9.1
17431  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
17432  TLSTEntry.Location = AVE.LocationName;;
17433  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
17434  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
17435  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
17436  TLSTEntry.ArrTime = ""; //need to reset this to null
17437  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
17438  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
17439  }
17440 
17441  else if(AVE.FormatType == ExitRailway) //Fer
17442  {
17443  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
17444  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
17445  //be wrong, but can't guess from here & most will have same name
17446  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
17447  if(LName != "")
17448  {
17449  TLSTEntry.Location = LName;
17450  }
17451  else
17452  {
17453  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
17454  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
17455  AnsiString HString;
17456  AnsiString VString;
17457  if(HLoc < 0)
17458  {
17459  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17460  }
17461  else
17462  {
17463  HString = AnsiString(HLoc);
17464  }
17465  if(VLoc < 0)
17466  {
17467  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17468  }
17469  else
17470  {
17471  VString = AnsiString(VLoc);
17472  }
17473  TLSTEntry.Location = HString + '-' + VString;
17474  }
17475  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
17476  }
17477 
17478  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
17479  {
17480  AnsiString FrhTime;
17481  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17482  {
17483  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17484  }
17485  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17486  {
17487  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17488  }
17489  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
17490  TLSTEntry.Location = AVE.LocationName;
17491  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17492  TLSTEntry.FrhMarker = "Frh";
17493  LocServiceTimesVector.push_back(TLSTEntry);
17494  TLSTEntry.FrhMarker = "";
17495  //add all times from next minute to end of timetable
17496  while(IncTime <= LastTTTime)
17497  {
17498  TLSTEntry.AtLocTime = IncTime;
17499  LocServiceTimesVector.push_back(TLSTEntry);
17500  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17501  }
17502  }
17503 
17504  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
17505  {
17506  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
17507  {
17508  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
17509  TLSTEntry.Location = AVE.LocationName;
17510  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17511  TLSTEntry.FrhMarker = "Frh";
17512  LocServiceTimesVector.push_back(TLSTEntry);
17513  TLSTEntry.FrhMarker = "";
17514  //add all times from next minute to end of timetable
17515  while(IncTime <= LastTTTime)
17516  {
17517  TLSTEntry.AtLocTime = IncTime;
17518  LocServiceTimesVector.push_back(TLSTEntry);
17519  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17520  }
17521  }
17522  }
17523 
17524  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
17525  {
17526  //nothing is done here as the entry will be listed at this time under the new service reference
17527  }
17528  }
17529  }
17530  }
17531  SequenceLog += "5\n";
17532  //now sort in location order
17533  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
17534  //LocServiceTimesVector now complete & sorted in location order
17535 
17536 /*
17537 //start of debugging section
17539  std::ofstream LSTVFile("LSTVFile.txt");
17540  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
17541  {
17542  LSTVFile << LSTVIt->Location + '\n';
17543  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
17544  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
17545  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
17546  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
17547  if(LSTVIt->FrhMarker == "")
17548  {
17549  LSTVFile << "Not Frh\n";
17550  }
17551  else
17552  {
17553  LSTVFile << LSTVIt->FrhMarker + '\n';
17554  }
17555  LSTVFile << '\n';
17556  }
17557  LSTVFile.close();
17558 //end of debugging section
17559 */
17560 
17561  //declare pointers for use in printouts
17562  TLocServiceTimesVector::iterator Ptr1, Ptr2;
17563 
17564  //set up the output file
17565  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17566  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17567 
17568  std::ofstream TTFile3(TTFileName3.c_str());
17569 
17570  if(TTFile3 == 0)
17571  {
17572  ShowMessage("Conflict Analysis file failed to open - can't be created");
17573  Utilities->CallLogPop(2210);
17574  return(false);
17575  }
17576  if(LocServiceTimesVector.empty())
17577  {
17578  ShowMessage("No timetabled services found");
17579  TTFile3.close();
17580  DeleteFile(TTFileName3);
17581  Utilities->CallLogPop(2211);
17582  return(false);
17583  }
17584  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
17585  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
17586  SequenceLog += "6\n";
17587 
17588 /*
17589 //print out TrainDataVectorCopy for debugging purposes NB THIS USES TDateTime.TimeString, which rolls over at 24 hours, use Format96 instead
17590 std::ofstream TDVCFile("TDVCFile.txt");
17591 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
17592 {
17593  TDVCFile << TDVCIt->ServiceReference + '\n';
17594  TDVCFile << TDVCIt->Description + '\n';
17595  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
17596  {
17597  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
17598  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
17599  {
17600  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
17601  }
17602  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
17603  {
17604  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
17605  }
17606  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
17607  {
17608  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
17609  }
17610  else if(AVE.FormatType == TimeTimeLoc)
17611  {
17612  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
17613  }
17614  else if(AVE.FormatType == PassTime)
17615  {
17616  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
17617  }
17618  else if(AVE.FormatType == ExitRailway)
17619  {
17620  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
17621  }
17622  else if(AVE.FormatType == FinRemHere)
17623  {
17624  TDVCFile << "Frh" << '\n';
17625  }
17626  }
17627  TDVCFile << '\n';
17628 }
17629 TDVCFile.close();
17630 */
17631 
17632  //arrivals
17633  if(ArrChecked)
17634  {
17635  //sort in ArrTime order for each location
17636  Ptr1 = LocServiceTimesVector.begin();
17637  Ptr2 = Ptr1 + 1;
17638  while(Ptr2 != LocServiceTimesVector.end())
17639  {
17640  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17641  {
17642  Ptr2++;
17643  if(Ptr2 == LocServiceTimesVector.end())
17644  {
17645  break;
17646  }
17647  }
17648  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
17649  Ptr1 = Ptr2; //first entry with next name
17650  if(Ptr2 != LocServiceTimesVector.end())
17651  {
17652  Ptr2++;
17653  }
17654  }
17655 
17656  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
17657 
17658  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
17659  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17660  MinuteString = " minutes";
17661  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17662  if(ArrRange == 1)
17663  {
17664  MinuteString = " minute";
17665  }
17666  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
17667  TTFile3 << ",Platforms,Trains\n\n";
17668 
17669  Ptr1 = LocServiceTimesVector.begin();
17670  Ptr2 = Ptr1 + 1;
17671  while(Ptr2 != LocServiceTimesVector.end())
17672  {
17673  PreviousService = "";
17674  NumTrainsAtLoc = 0;
17675  ServiceAndRepeatNumTotal = "";
17676  NumPlats = 0;
17677  NumPlatsAtThisLocCalculated = false;
17678  BasicTime = "";
17679  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17680  {
17681  PreviousService = "";
17682  NumTrainsAtLoc = 0;
17683  ServiceAndRepeatNumTotal = "";
17684  NumPlats = 0;
17685  NumPlatsAtThisLocCalculated = false;
17686  BasicTime = "";
17687  Ptr1++;
17688  Ptr2++;
17689  if(Ptr2 == LocServiceTimesVector.end())
17690  {
17691  break;
17692  }
17693  }
17694  if(Ptr2 == LocServiceTimesVector.end())
17695  {
17696  break;
17697  }
17698  while(Ptr2->Location == Ptr1->Location)
17699  {
17700  PreviousService = "";
17701  NumTrainsAtLoc = 0;
17702  ServiceAndRepeatNumTotal = "";
17703  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17704  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17705  {
17706  break;
17707  }
17708  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
17709  {
17710  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
17711  Ptr1++;
17712  Ptr2++;
17713  if(Ptr2 == LocServiceTimesVector.end())
17714  {
17715  break;
17716  }
17717  if(Ptr2->Location != Ptr1->Location)
17718  {
17719  break;
17720  }
17721  }
17722  if(Ptr2 == LocServiceTimesVector.end())
17723  {
17724  break;
17725  }
17726  if(Ptr2->Location != Ptr1->Location)
17727  {
17728  break;
17729  }
17730  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
17731  {
17732  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
17733  {
17734  break;
17735  }
17736  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17737  {
17738  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
17739  NumPlatsAtThisLocCalculated = true;
17740  }
17741  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17742  {
17743  if(ServiceAndRepeatNumTotal == "")
17744  {
17745  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17746  NumTrainsAtLoc = 1;
17747  }
17748  else
17749  {
17750  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17751  }
17752  }
17753  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17754  if(ServiceAndRepeatNumTotal == "")
17755  {
17756  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17757  NumTrainsAtLoc = 1;
17758  }
17759  else
17760  {
17761  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17762  }
17763  Ptr1 = Ptr2;
17764  Ptr2++;
17765  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
17766  {
17767  int MaxNumberOfSameDirections = 0;
17768  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17769  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17770  {
17771 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17772  TTFile3.close();
17773  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17774 // Utilities->CallLogPop(2224);
17775 // return false;
17776  }
17777  AnsiString Asterisk = "";
17778  if(MaxNumberOfSameDirections >= NumPlats)
17779  {
17780  Asterisk = "* ";
17781  }
17782  //print out a single line for number of trains at loc with all service refs
17783  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17784  ArrivalsPrinted = true;
17785  ServiceAndRepeatNumTotal = "";
17786  }
17787  if(Ptr2 == LocServiceTimesVector.end())
17788  {
17789  break;
17790  }
17791  if(Ptr2->Location != Ptr1->Location)
17792  {
17793  break;
17794  }
17795  }
17796  if(Ptr2 == LocServiceTimesVector.end())
17797  {
17798  break;
17799  }
17800  }
17801  }
17802  if(!ArrivalsPrinted)
17803  {
17804  TTFile3 << "Nothing to report for arrivals";
17805  }
17806  TTFile3 << "\n\n";
17807  }
17808  //end of routine for arrivals
17809  SequenceLog += "7\n";
17810  //departures
17811  if(DepChecked)
17812  {
17813  //sort in DepTime order for each location
17814  Ptr1 = LocServiceTimesVector.begin();
17815  Ptr2 = Ptr1 + 1;
17816  while(Ptr2 != LocServiceTimesVector.end())
17817  {
17818  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17819  {
17820  Ptr2++;
17821  if(Ptr2 == LocServiceTimesVector.end())
17822  {
17823  break;
17824  }
17825  }
17826  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
17827  Ptr1 = Ptr2; //first entry with next name
17828  if(Ptr2 != LocServiceTimesVector.end())
17829  {
17830  Ptr2++;
17831  }
17832  }
17833 
17834  //routine for departures - number of trains departing within the specified range with services listed at the end
17835  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
17836  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17837  MinuteString = " minutes";
17838  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17839  if(DepRange == 1)
17840  {
17841  MinuteString = " minute";
17842  }
17843  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
17844  TTFile3 << ",Platforms,Trains\n\n";
17845 
17846  Ptr1 = LocServiceTimesVector.begin();
17847  Ptr2 = Ptr1 + 1;
17848  while(Ptr2 != LocServiceTimesVector.end())
17849  {
17850  PreviousService = "";
17851  NumTrainsAtLoc = 0;
17852  ServiceAndRepeatNumTotal = "";
17853  NumPlats = 0;
17854  NumPlatsAtThisLocCalculated = false;
17855  BasicTime = "";
17856  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17857  {
17858  PreviousService = "";
17859  NumTrainsAtLoc = 0;
17860  ServiceAndRepeatNumTotal = "";
17861  NumPlats = 0;
17862  NumPlatsAtThisLocCalculated = false;
17863  BasicTime = "";
17864  Ptr1++;
17865  Ptr2++;
17866  if(Ptr2 == LocServiceTimesVector.end())
17867  {
17868  break;
17869  }
17870  }
17871  if(Ptr2 == LocServiceTimesVector.end())
17872  {
17873  break;
17874  }
17875  while(Ptr2->Location == Ptr1->Location)
17876  {
17877  PreviousService = "";
17878  NumTrainsAtLoc = 0;
17879  ServiceAndRepeatNumTotal = "";
17880  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17881  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17882  {
17883  break;
17884  }
17885  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
17886  {
17887  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
17888  Ptr1++;
17889  Ptr2++;
17890  if(Ptr2 == LocServiceTimesVector.end())
17891  {
17892  break;
17893  }
17894  if(Ptr2->Location != Ptr1->Location)
17895  {
17896  break;
17897  }
17898  }
17899  if(Ptr2 == LocServiceTimesVector.end())
17900  {
17901  break;
17902  }
17903  if(Ptr2->Location != Ptr1->Location)
17904  {
17905  break;
17906  }
17907  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
17908  {
17909  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
17910  {
17911  break;
17912  }
17913  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17914  {
17915  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
17916  NumPlatsAtThisLocCalculated = true;
17917  }
17918  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17919  {
17920  if(ServiceAndRepeatNumTotal == "")
17921  {
17922  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17923  NumTrainsAtLoc = 1;
17924  }
17925  else
17926  {
17927  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17928  }
17929  }
17930  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17931  if(ServiceAndRepeatNumTotal == "")
17932  {
17933  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17934  NumTrainsAtLoc = 1;
17935  }
17936  else
17937  {
17938  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17939  }
17940  Ptr1 = Ptr2;
17941  Ptr2++;
17942  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
17943  {
17944  int MaxNumberOfSameDirections = 0;
17945  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17946  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17947  {
17948 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17949  TTFile3.close();
17950  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17951 // Utilities->CallLogPop(2225);
17952 // return false;
17953  }
17954  AnsiString Asterisk = "";
17955  if(MaxNumberOfSameDirections >= NumPlats)
17956  {
17957  Asterisk = "* ";
17958  }
17959  //print out a single line for number of trains at loc with all service refs
17960  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17961  DeparturesPrinted = true;
17962  ServiceAndRepeatNumTotal = "";
17963  }
17964  if(Ptr2 == LocServiceTimesVector.end())
17965  {
17966  break;
17967  }
17968  if(Ptr2->Location != Ptr1->Location)
17969  {
17970  break;
17971  }
17972  }
17973  if(Ptr2 == LocServiceTimesVector.end())
17974  {
17975  break;
17976  }
17977  }
17978  }
17979  if(!DeparturesPrinted)
17980  {
17981  TTFile3 << "Nothing to report for departures";
17982  }
17983  TTFile3 << "\n\n";
17984  }
17985  //end of routine for departures
17986  SequenceLog += "8\n";
17987 
17988  //list trains at locations at same time
17989 
17990  if(AtLocChecked)
17991  {
17992  //sort in AtLocTime order for each location
17993  Ptr1 = LocServiceTimesVector.begin();
17994  Ptr2 = Ptr1 + 1;
17995  while(Ptr2 != LocServiceTimesVector.end())
17996  {
17997  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17998  {
17999  Ptr2++;
18000  if(Ptr2 == LocServiceTimesVector.end())
18001  {
18002  break;
18003  }
18004  }
18005  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
18006  Ptr1 = Ptr2; //first entry with next name
18007  if(Ptr2 != LocServiceTimesVector.end())
18008  {
18009  Ptr2++;
18010  }
18011  }
18012 
18013  //print out simultaneous AtLocs (don't need range of times for AtLocs)
18014  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
18015  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
18016  TTFile3 << ",Platforms,Trains,\n\n";
18017  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
18018  Ptr1 = LocServiceTimesVector.begin();
18019  Ptr2 = Ptr1 + 1;
18020  while(Ptr2 != LocServiceTimesVector.end())
18021  {
18022  PreviousService = "";
18023  ServiceAndRepeatNumTotal = "";
18024  NumTrainsAtLoc = 0;
18025  NumPlats = 0;
18026  NumPlatsAtThisLocCalculated = false;
18027  FrhCount = 0;
18028 
18029  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
18030  {
18031  PreviousService = "";
18032  ServiceAndRepeatNumTotal = "";
18033  NumTrainsAtLoc = 0;
18034  NumPlats = 0;
18035  NumPlatsAtThisLocCalculated = false;
18036  FrhCount = 0;
18037  Ptr1++;
18038  Ptr2++;
18039  if(Ptr2 == LocServiceTimesVector.end())
18040  {
18041  break;
18042  }
18043  }
18044  if(Ptr2 == LocServiceTimesVector.end())
18045  {
18046  break;
18047  }
18048  while(Ptr2->Location == Ptr1->Location)
18049  {
18050  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
18051  {
18052  FrhCount++;
18053  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
18054  }
18055  PreviousService = "";
18056  NumTrainsAtLoc = 0;
18057  ServiceAndRepeatNumTotal = "";
18058  if((Ptr1->Location == "") && (Ptr2->Location == ""))
18059  {
18060  break;
18061  }
18062  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
18063  {
18064  Ptr1++;
18065  if(Ptr1->FrhMarker == "Frh")
18066  {
18067  FrhCount++;
18068  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
18069  }
18070  Ptr2++;
18071  if(Ptr2 == LocServiceTimesVector.end())
18072  {
18073  break;
18074  }
18075  if(Ptr2->Location != Ptr1->Location)
18076  {
18077  break;
18078  }
18079  }
18080  if(Ptr2 == LocServiceTimesVector.end())
18081  {
18082  break;
18083  }
18084  if(Ptr2->Location != Ptr1->Location)
18085  {
18086  break;
18087  }
18088  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
18089  {
18090  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
18091  {
18092  break;
18093  }
18094  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
18095  {
18096  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
18097  NumPlatsAtThisLocCalculated = true;
18098  }
18099  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
18100  {
18101  if(ServiceAndRepeatNumTotal == "")
18102  {
18103  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
18104  NumTrainsAtLoc = 1;
18105  }
18106  else
18107  {
18108  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
18109  }
18110  }
18111  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
18112  if(ServiceAndRepeatNumTotal == "")
18113  {
18114  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
18115  NumTrainsAtLoc = 1;
18116  }
18117  else
18118  {
18119  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
18120  }
18121  Ptr1 = Ptr2;
18122  if(Ptr1->FrhMarker == "Frh")
18123  {
18124  FrhCount++;
18125  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
18126  }
18127  Ptr2++;
18128  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
18129  {
18130 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
18131 //new text //don't print out if all remainers or if only 1 train at loc
18132  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
18133 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
18134 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
18135  {
18136  AnsiString Asterisk = "";
18137  if(NumTrainsAtLoc > NumPlats)
18138  {
18139  Asterisk = "* ";
18140  }
18141  //print out a single line for number of trains at loc with all service refs
18142  if(FrhCount == 0)
18143  {
18144  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
18145  }
18146  else if(FrhCount == 1)
18147  {
18148  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
18149  }
18150  else
18151  {
18152  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
18153  }
18154  LastFrhCount = FrhCount;
18155  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
18156  AtLocsPrinted = true;
18157  ServiceAndRepeatNumTotal = "";
18158  }
18159  }
18160  if(Ptr2 == LocServiceTimesVector.end())
18161  {
18162  break;
18163  }
18164  if(Ptr2->Location != Ptr1->Location)
18165  {
18166  break;
18167  }
18168  }
18169  if(Ptr2 == LocServiceTimesVector.end())
18170  {
18171  break;
18172  }
18173  }
18174  }
18175  if(!AtLocsPrinted)
18176  {
18177  TTFile3 << "Nothing to report for trains at locations";
18178  }
18179  TTFile3 << "\n\n";
18180  //end of simultaneous AtLocs
18181  }
18182  SequenceLog += "9\n";
18183 /*
18184  //print out the full vector here for testing purposes
18185  TTFile3 << "Full LocServiceTimesVector\n\n";
18186  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
18187 
18188  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
18189  {
18190  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
18191  }
18192 
18193  TTFile3 << "\n\n\n";
18194 */
18195 /*cdt analysis - added at v2.10.0
18196 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
18197 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
18198 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
18199 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
18200 service.
18201 
18202 Use The TrainDataVectorCopy as that has all unique service refs.
18203 
18204 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
18205 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
18206 
18207 First create a new TrainDataVector from earlier copy as above with single services
18208 */
18209  if(DirChecked)
18210  {
18211  //direction analysis added at v2.10.0
18212  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
18213  TTrainDataVector SingleServiceVector, PartServiceVector;
18214 
18215  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
18216  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
18217  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
18218  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18219  {
18220  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
18221  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
18222  {
18223  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
18224  }
18225  const TActionVector &AV = TDE.ActionVector;
18226  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
18227  {
18228  SingleServiceEntry = TDE;
18229  TActionVector &SSAV = SingleServiceEntry.ActionVector;
18230  for(unsigned int y = 0; y < SSAV.size(); y++)
18231  {
18232  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
18233  {
18234  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
18235  break; //finished with this one
18236  }
18237  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
18238  {
18239  PartServiceEntry = TDE; //start with complete entry
18240  PartServiceEntry.ActionVector.clear(); //clear AV
18241  for(unsigned int z = 0; z <= y; z++)
18242  {
18243  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
18244  if(z == y)
18245  {
18246  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
18247  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
18248  }
18249  }
18250  PartServiceVector.push_back(PartServiceEntry);
18251  if(SSAV.at(y).Command == "fsp")
18252  {
18253  SSAV.at(y).Command = "Front split - original service continues below";
18254  SSAV.at(y).OtherHeadCode = "";
18255  }
18256  if(SSAV.at(y).Command == "rsp")
18257  {
18258  SSAV.at(y).Command = "Rear split - original service continues below";
18259  SSAV.at(y).OtherHeadCode = "";
18260  }
18261  //don't break & continue here because the original train carries on
18262  }
18263  else if(SSAV.at(y).Command == "Fns")
18264  {
18265  SSAV.at(y).Command = "chr-Fns";
18266  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
18267  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
18268  break; //from y loop
18269  }
18270  else if(SSAV.at(y).Command == "Fns-sh")
18271  {
18272  SSAV.at(y).Command = "chr-Fns-sh";
18273  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18274  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
18275  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
18276  break; //from y loop
18277  }
18278  else if(SSAV.at(y).Command == "F-nshs")
18279  {
18280  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
18281  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
18282  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
18283  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
18284  break; //from y loop
18285  }
18286  }
18287  }
18288  }
18289  SequenceLog += "10\n";
18290  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
18291 
18292  //Now add Sns & Sns-sh services to PartServiceVector entries
18293  AnsiString NextRef;
18294  while(!PartServiceVector.empty())
18295  {
18296  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
18297  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
18298  {
18299  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
18300  {
18301  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
18302  }
18303  }
18304  //find it in TrainDataVectorCopy
18305  bool FinishType = true, FoundFlag = false;
18306  while(FinishType)
18307  {
18308  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
18309  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
18310  if(FoundFlag)
18311  {
18312  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
18313  {
18314  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
18315  {
18316  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18317  }
18318  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
18319  {
18320  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18321  }
18322  else
18323  {
18324  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
18325  {
18326  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18327  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
18328  PartServiceVector.erase(&PartServiceVector.at(0));
18329  break; //from y loop
18330  }
18331  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
18332  {
18333  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
18334  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
18335  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
18336  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
18337  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
18338  if(TempEntry.ActionVector.at(y).Command == "fsp")
18339  {
18340  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
18341  TempEntry.ActionVector.at(y).OtherHeadCode = "";
18342  }
18343  if(TempEntry.ActionVector.at(y).Command == "rsp")
18344  {
18345  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
18346  TempEntry.ActionVector.at(y).OtherHeadCode = "";
18347  }
18348  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18349  }
18350  else if(TempEntry.ActionVector.at(y).Command == "Fns")
18351  {
18352  TempEntry.ActionVector.at(y).Command = "chr-Fns";
18353  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18354  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
18355  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18356  break; //from y loop
18357  }
18358  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
18359  {
18360  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
18361  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18362  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
18363  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18364  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18365  break; //from y loop
18366  }
18367  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
18368  {
18369  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
18370  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18371  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
18372  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18373  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18374  break; //from y loop
18375  }
18376  }
18377  }
18378  }
18379  else
18380  {
18381  SequenceLog += + "11\n";
18382  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
18383  }
18384  }
18385  }
18386  if(!PartServiceVector.empty())
18387  {
18388  SequenceLog += "12\n";
18389  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
18390  }
18391  SequenceLog += "13\n";
18392  /*
18393  form:-
18394  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
18395  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
18396  then multiple entries, separated by commas, of the form:-
18397 
18398  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
18399  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
18400  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
18401 
18402  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
18403  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
18404  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
18405 
18406  HH:MM;Command (cdt) }TimeCmd }
18407  HH:MM;Location (arr & dep) }TimeLoc }
18408  HH:MM;HH:MM;Location }TimeTimeLoc }
18409  HH:MM;pas;Location }PassTime }
18410  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
18411  HH:MM;Fer;set of allowable IDs }ExitRailway }
18412  Command (Frh only) }FinRemHere }
18413 
18414  R;mm;dd;nn. Repeat Repeat entry
18415 
18416  Formats:
18417 
18418  Command only: Frh
18419  Time;Command: cdt
18420  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
18421  Time;Command;2 Element IDs: Snt
18422  Time;Comand;n Element IDs: Fer
18423  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
18424  Time;Command;2 Element IDs;Headcode Snt-sh
18425  Time;Command;Location pas
18426  Time;Location Arr Dep
18427  Time;Time;Location Arr & dep together
18428  */
18429 
18430 /*
18431 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
18432 checking forwards until it comes to a continuation (no report), a location name that is not null and
18433 different to the train's front element name (whether null or not) (no report), a leading point
18434 (no report) or buffers (report).
18435 */
18436  bool BufferFacingUnReportedFlag = true;
18437  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18438  {
18439  TTrackElement ThisElement, NextElement;
18440  TTrainDataEntry TDE = SingleServiceVector.at(x);
18441  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
18442  {
18443  SequenceLog += "13a\n";
18444  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
18445  }
18446  const TActionVector &AV = TDE.ActionVector;
18447  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
18448  {
18449  bool BufferFlag = false;
18450  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
18451  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
18452  AnsiString FrontLocName = AV.at(0).LocationName;
18453  int NextEntryPos, NextExitPos;
18454  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
18455  int ThisExitPos;
18456  if(ThisElement.Conn[0] == RearTVPos)
18457  {
18458  ThisExitPos = 1;
18459  }
18460  else if(ThisElement.Conn[1] == RearTVPos)
18461  {
18462  ThisExitPos = 0;
18463  }
18464  else if(ThisElement.Conn[2] == RearTVPos)
18465  {
18466  ThisExitPos = 3;
18467  }
18468  else if(ThisElement.Conn[3] == RearTVPos)
18469  {
18470  ThisExitPos = 2;
18471  }
18472  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
18473  {
18474  BufferFlag = true;
18475  }
18476  else //continue tracking forwards
18477  {
18478  while(true)
18479  {
18480  if(ThisElement.Conn[ThisExitPos] == -1)
18481  {
18482  SequenceLog = "13b\n";
18483  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
18484  }
18485  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
18486  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
18487  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
18488  {
18489  BufferFlag = false; //should already be false
18490  break;
18491  }
18492  else if(NextElement.TrackType == Continuation)
18493  {
18494  BufferFlag = false;
18495  break;
18496  }
18497  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
18498  {
18499  BufferFlag = false;
18500  break;
18501  }
18502  else if(NextElement.TrackType == Buffers)
18503  {
18504  BufferFlag = true;
18505  break;
18506  }
18507  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
18508  {
18509  ThisElement = NextElement;
18510  ThisExitPos = 0;
18511  continue;
18512  }
18513  else
18514  {
18515  if(NextEntryPos == 0)
18516  {
18517  NextExitPos = 1;
18518  }
18519  else if(NextEntryPos == 1)
18520  {
18521  NextExitPos = 0;
18522  }
18523  else if(NextEntryPos == 2)
18524  {
18525  NextExitPos = 3;
18526  }
18527  else if(NextEntryPos == 3)
18528  {
18529  NextExitPos = 2;
18530  }
18531  }
18532  ThisElement = NextElement;
18533  ThisExitPos = NextExitPos;
18534  }
18535  }
18536  if(BufferFlag)
18537  {
18538  if(BufferFacingUnReportedFlag)
18539  {
18540  TTFile3 << "Train facing direction on creation analysis:-\n\n";
18541  BufferFacingUnReportedFlag = false;
18542  }
18543  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
18544  }
18545  }
18546  }
18547  if(BufferFacingUnReportedFlag)
18548  {
18549  TTFile3 << "Nothing to report for train facing directions\n\n";
18550  }
18551  else
18552  {
18553  TTFile3 << '\n';
18554  }
18555  SequenceLog += "13c\n";
18556 
18557  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
18558  AnsiString LocationNameToBeChecked = "";
18559  bool MissingcdtUnreportedFlag = true;
18560  TNumList MarkerList;
18561  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18562  {
18563  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18564  unsigned int y = 0;
18565  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
18566  bool FullBreak = false;
18567  MarkerList.clear();
18568  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
18569  // first discard unlocated Snt entries as they don't have location name set
18570  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
18571  {
18572  y = 1;
18573  }
18574  while((y < TDEntry.ActionVector.size()) && !FullBreak)
18575  // need to check each location name separately in turn, skipped for SignallerControl entries
18576  {
18577  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
18578  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
18579  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
18580  {
18581  break; // out of the 'while' loop since have reached the end
18582  }
18583  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
18584  FirstInstance = y;
18585  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
18586  {
18587  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18588  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
18589  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18590  (AVEntry.Command == "Frh-sh"))
18591  {
18592  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
18593  }
18594  if(AVEntry.Command == "cdt")
18595  {
18596  break; // out of the 'z' loop since the check is only valid up to a change of direction
18597  }
18598  if(AVEntry.LocationName == LocationNameToBeChecked)
18599  {
18600  continue; // keep going while name same
18601  }
18602  if(AVEntry.LocationName != LocationNameToBeChecked)
18603  // if name different check forwards to see if repeats
18604  {
18605  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
18606  {
18607  if(TDEntry.ActionVector.at(a).Command == "cdt")
18608  {
18609  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
18610  }
18611  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
18612  {
18613  SecondInstance = a;
18614  AnsiString Sequence = TDEntry.ServiceReference;
18615  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18616  {
18617  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18618  {
18619  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18620  }
18621  }
18622  if(MissingcdtUnreportedFlag)
18623  {
18624  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
18625  }
18626  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
18627  MarkerList.push_back(FirstInstance);
18628  MarkerList.push_back(SecondInstance);
18629  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
18630  MissingcdtUnreportedFlag = false;
18631  FullBreak = true; //no more checks for this sequence
18632  break; //out of the a & z loops
18633  }
18634  }
18635  break; // out of the 'z' loop since have checked 'a' as far as need to
18636  }
18637  }
18638  y++;
18639  }
18640  }
18641  if(MissingcdtUnreportedFlag)
18642  {
18643  TTFile3 << "Nothing to report for missing changes of direction\n\n";
18644  }
18645  else
18646  {
18647  TTFile3 << '\n';
18648  }
18649  SequenceLog += "14\n";
18650 
18651 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
18652  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
18653  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
18654  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
18655  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
18656  included in both then ok, else report as questionable. If one list is empty then it is reported.
18657 */
18658  typedef std::list<AnsiString> TLocList;
18659  TLocList BackwardList, ForwardList;
18660  bool IntroLineNeeded = true;
18661  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18662  {
18663  unsigned int cdtPosition = 9999;
18664  AnsiString cdtLocation = "";
18665  bool FoundSameName = false;
18666  MarkerList.clear();
18667  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18668  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
18669  // need to check each location name separately in turn, skipped for SignallerControl entries
18670  {
18671  BackwardList.clear();
18672  ForwardList.clear();
18673  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
18674  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
18675  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18676  (AVEntry.Command == "Frh-sh"))
18677  {
18678  if(MarkerList.empty())
18679  {
18680  break; // out of the 'y' loop since have reached the end & nothing to report
18681  }
18682  else
18683  {
18684  AnsiString Sequence = TDEntry.ServiceReference;
18685  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18686  {
18687  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18688  {
18689  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18690  }
18691  }
18692  MarkerList.sort();
18693  if(IntroLineNeeded)
18694  {
18695  TTFile3 << "Questionable change of direction analysis.\n\n";
18696  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
18697  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
18698  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
18699  TTFile3 << "make sure that none has been included incorrectly:\n\n";
18700  IntroLineNeeded = false;
18701  }
18702  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
18703  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
18704  break;
18705  }
18706  }
18707  if(AVEntry.Command != "cdt")
18708  {
18709  continue; //only looking for cdts
18710  }
18711  //here have found a cdt
18712  cdtPosition = y;
18713  cdtLocation = AVEntry.LocationName;
18714  for(int z = y - 1; z >= 0; z--)
18715  {
18716  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
18717  if(AVEntry2.Command == "cdt")
18718  {
18719  break; //don't look further back than the last cdt
18720  }
18721  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
18722  {
18723  BackwardList.push_back(AVEntry2.LocationName);
18724  }
18725  }
18726  BackwardList.sort();
18727  BackwardList.unique();
18728  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
18729  {
18730  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
18731  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
18732  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
18733  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
18734  {
18735  break; // out of the 'z' loop since have reached another cdt or the end
18736  }
18737  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
18738  {
18739  ForwardList.push_back(AVEntry3.LocationName);
18740  }
18741  }
18742  ForwardList.sort();
18743  ForwardList.unique();
18744  FoundSameName = false;
18745  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
18746  if(!BackwardList.empty() && !ForwardList.empty())
18747  {
18748  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
18749  {
18750  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
18751  {
18752  if(*BLIt == *FLIt)
18753  {
18754  FoundSameName = true;
18755  }
18756  }
18757  }
18758  }
18759  if(!FoundSameName) //report the inability to find same name
18760  {
18761  MarkerList.push_back(cdtPosition);
18762  }
18763  }
18764  }
18765  if(IntroLineNeeded)
18766  {
18767  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
18768  }
18769  else
18770  {
18771  TTFile3 << '\n';
18772  }
18773 /*
18774 //print all SSVector for diagnostic purposes
18775  TTFile3 << "Whole SSVector\n\n";
18776  TNumList EmptyList;
18777  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18778  {
18779  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
18780  }
18781 */
18782  }
18783  SequenceLog += "15\n";
18784  TTFile3.close();
18785  Utilities->CallLogPop(2212);
18786  return(true);
18787  }
18788 
18789  catch(const Exception &e) //non error catch
18790  {
18791  AnsiString TTErrorFileName = "Analysis Error.txt";
18792  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
18793  std::ofstream TTError(TTErrorFileName.c_str());
18794  if(TTError == 0)
18795  {
18796  ShowMessage("Analysis error file failed to open - can't be created");
18797  Utilities->CallLogPop(2233);
18798  return(false);
18799  }
18800  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18801  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
18802  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
18803 
18804  TTError.close();
18805  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
18806  Utilities->CallLogPop(2226);
18807  return(false);
18808  }
18809 }
18810 
18811 // ---------------------------------------------------------------------------
18812 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
18813 {
18814  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
18815  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
18816  {
18817  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
18818  }
18819  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
18820  {
18821  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
18822  AnsiString Marker = "";
18823  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
18824  {
18825  Marker = ',';
18826  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
18827  {
18828  if(int(x) == *MLIt)
18829  {
18830  Marker = "-->,";
18831  break;
18832  }
18833  }
18834  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
18835  if(AVE.FormatType == StartNew)
18836  {
18837  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
18838  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
18839  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
18840  }
18841  if(AVE.FormatType == SNTShuttle)
18842  {
18843  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
18844  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
18845  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
18846  }
18847  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
18848  {
18849  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
18850  }
18851  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
18852  {
18853  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
18854  }
18855  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
18856  {
18857  TActionVectorEntry AVHolder = AVE;
18858  if(AVE.Command.SubString(1,3) == "chr")
18859  {
18860  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
18861  {
18862  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
18863  AVE.OtherHeadCode = "";
18864  }
18865  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
18866  {
18867  AVE.Command = "Change of service to ";
18868  }
18869  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
18870  {
18871  AVE.Command = "Change to shuttle finishing service";
18872  }
18873  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
18874  {
18875  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
18876  AVE.OtherHeadCode = "";
18877  }
18878  }
18879  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
18880  AVE = AVHolder;
18881  }
18882  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18883  {
18884  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18885  }
18886  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18887  {
18888  VecFile << Marker << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18889  }
18890  else if(AVE.FormatType == TimeTimeLoc)
18891  {
18892  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18893  }
18894  else if(AVE.FormatType == PassTime)
18895  {
18896  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18897  }
18898  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
18899  {
18900  AnsiString ListOfExits = "";
18901  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
18902  {
18903  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
18904  }
18905  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << " Fer " << ListOfExits <<'\n';
18906  }
18907  else if(AVE.FormatType == FinRemHere)
18908  {
18909  VecFile << Marker << "Frh" << '\n';
18910  }
18911  }
18912  VecFile << '\n';
18913  }
18914  Utilities->CallLogPop(2318);
18915 }
18916 
18917 // ---------------------------------------------------------------------------
18918 
18919 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
18920 {
18921  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
18922  FoundFlag = false;
18923  FinishType = true;
18924  for(unsigned int x = 0; x < Vector.size(); x++)
18925  {
18926  if(Vector.at(x).ServiceReference == ServiceReference)
18927  {
18928  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
18929  {
18930  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
18931  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18932  {
18933  FinishType = false;
18934  }
18935  }
18936  else
18937  {
18938  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
18939  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18940  {
18941  FinishType = false;
18942  }
18943  }
18944  FoundFlag = true;
18945  Utilities->CallLogPop(2319);
18946  return(Vector.at(x));
18947  }
18948  }
18949  Utilities->CallLogPop(2320);
18950  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
18951 }
18952 
18953 // ---------------------------------------------------------------------------
18954 
18955 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
18956 {
18957 //convert times to integer minutes
18958  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
18959  if((Time1 == "") || (Time2 == ""))
18960  {
18961  Utilities->CallLogPop(2213);
18962  return(false);
18963  }
18964  int Mins = Time1.SubString(4,2).ToInt();
18965  int Hours = Time1.SubString(1,2).ToInt();
18966  int Time1Mins = (Hours * 60) + Mins;
18967  Mins = Time2.SubString(4,2).ToInt();
18968  Hours = Time2.SubString(1,2).ToInt();
18969  int Time2Mins = (Hours * 60) + Mins;
18970  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
18971  {
18972  Utilities->CallLogPop(2214);
18973  return(true);
18974  }
18975  Utilities->CallLogPop(2215);
18976  return(false);
18977 }
18978 
18979 // ---------------------------------------------------------------------------
18980 
18981 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
18982  bool &AnalysisError, int &MaxNumberOfSameDirections)
18983 {
18984  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
18985 
18986  try
18987  {
18988  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
18989  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
18990  int SCPos = 0;
18991  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
18992  //first change every second comma in Input to a semicolon so can separate services but keep times with services
18993  bool EvenComma = false;
18994  for(int x = 1; x <= Input.Length(); x++)
18995  {
18996  TempStr1 = Input[x];
18997  if(TempStr1 == AnsiString(',') && EvenComma)
18998  {
18999  TempStr2 += ';';
19000  }
19001  else
19002  {
19003  TempStr2 += Input[x];
19004  }
19005  if(TempStr1 == AnsiString(','))
19006  {
19007  EvenComma = !EvenComma;
19008  }
19009  }
19010  //load up the list of services with associated times
19011  while(TempStr2.Length() > 0)
19012  {
19013  SCPos = TempStr2.Pos(';');
19014  if(SCPos > 0) //0 if not found, as won't be when only one service left
19015  {
19016  OneService = TempStr2.SubString(1, SCPos - 1);
19017  ServiceList.push_back(OneService);
19018  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
19019  }
19020  else //no semicolon so looking at last (or only) element
19021  {
19022  ServiceList.push_back(TempStr2);
19023  TempStr2 = "";
19024  }
19025  }
19026  ServiceList.sort(); // alphabetical order
19027  ServiceList.unique(); //remove duplicates
19028  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
19029 
19030  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
19031  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
19032  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
19033  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
19034 
19035  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19036  {
19037  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
19038  }
19039  SLIt3 = ServiceList.end();
19040  SLIt3--; //so points to last element
19041  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
19042  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
19043  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
19044  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
19045  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
19046  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
19047 
19048  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
19049  {
19050  SLIt = SLIt1;
19051  SLIt++; //so points to one after SLIt1
19052  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
19053  {
19054  continue; //already allocated so skip to the next
19055  }
19056  else
19057  {
19058  CommaPos1 = SLIt1->Pos(','); //can't be 0
19059  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
19060  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
19061  SpacePos = ServiceRef1.Pos(' ');
19062  RepeatNum1 = 0;
19063  if(SpacePos > 0) //otherwise it's already correct
19064  {
19065  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
19066  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
19067  if(RepeatInfo1[1] == 'F')
19068  {
19069  RepeatNum1 = 0;
19070  }
19071  else
19072  {
19073  SpacePos = RepeatInfo1.Pos(' ');
19074  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
19075  }
19076  }
19077  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
19078  //but this includes the "&0" etc so need to strip these
19079  AmpersandPos = AnsiTime1.Pos('&');
19080  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
19081 
19082  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
19083  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
19084  {
19085  throw Exception("ASCLIt1 Error in " + Input);
19086  }
19087  ServiceCallingLocsList1 = ASCLIt1->second;
19088  AmpersandPos = SLIt1->Pos('&');
19089  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
19090  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
19091 
19092  SameDirectionCount = 1;
19093  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
19094  {
19095  CommaPos2 = SLIt2->Pos(','); //can't be 0
19096  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
19097  //but this contains "(First service..." etc so need to strip these
19098  SpacePos = ServiceRef2.Pos(' ');
19099  RepeatNum2 = 0;
19100  if(SpacePos > 0) //otherwise it's already correct
19101  {
19102  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
19103  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
19104  if(RepeatInfo2[1] == 'F')
19105  {
19106  RepeatNum2 = 0;
19107  }
19108  else
19109  {
19110  SpacePos = RepeatInfo2.Pos(' ');
19111  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
19112  }
19113  }
19114  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
19115  //but this includes the "&0" etc so need to strip these
19116  AmpersandPos = AnsiTime2.Pos('&');
19117  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
19118 
19119  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
19120  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
19121  {
19122  throw Exception("ASCLIt2 Error in " + Input);
19123  }
19124  ServiceCallingLocsList2 = ASCLIt2->second;
19125  //now compare the two
19126  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
19127  {
19128  int AmpersandPos = SLIt2->Pos('&');
19129  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
19130  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
19131  SameDirectionCount++;
19132  }
19133  }
19134  if(SameDirectionCount > MaxNumberOfSameDirections)
19135  {
19136  MaxNumberOfSameDirections = SameDirectionCount;
19137  }
19138  }
19139  }
19140 
19141  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
19142  {
19143  //any existing direction so allocate it now
19144  AmpersandPos = SLIt3->Pos('&');
19145  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
19146  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
19147  }
19148  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
19149  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19150  {
19151  //extract the DirectionMarker as an integer
19152  AmpersandPos = SLIt->Pos('&');
19153  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
19154  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
19155  DirectionMarker = DirectionMarkerString.ToInt();
19156  AnsiString DirectionSuffix = "";
19157  char c;
19158  if(DirectionMarker < 27)
19159  {
19160  c = 64 + DirectionMarker; //so 1 -> 'A'
19161  DirectionSuffix = "," + AnsiString(c);
19162  }
19163  else if(DirectionMarker < 53)
19164  {
19165  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
19166  DirectionSuffix = ",A" + AnsiString(c);
19167  }
19168  else
19169  {
19170  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
19171  }
19172  *SLIt = ServiceWithoutMarker + DirectionSuffix;
19173  }
19174  //now prepare the final consolidated output
19175  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19176  {
19177  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
19178  }
19179  if(Output.Length() > 0)
19180  {
19181  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
19182  }
19183  Utilities->CallLogPop(2216);
19184  return(Output);
19185  }
19186 
19187  catch(const Exception &e) //non error catch
19188  {
19189  AnalysisError = true;
19190  Utilities->CallLogPop(2227);
19191  return(e.Message);
19192  }
19193 }
19194 
19195 // ---------------------------------------------------------------------------
19196 
19197 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
19198 {
19199  //similar to above but doesn't include times in the input
19200  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
19201  AnsiString InternalInput = Input, Output = "", OneService = "";
19202  int CommaPos = 0;
19203  std::list<AnsiString> ServiceList;
19204  //load up the list
19205  while(InternalInput.Length() > 0)
19206  {
19207  CommaPos = InternalInput.Pos(',');
19208  if(CommaPos > 0) //0 if not found, as won't be when only one service left
19209  {
19210  OneService = InternalInput.SubString(1, CommaPos - 1);
19211  ServiceList.push_back(OneService);
19212  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
19213  }
19214  else //no comma so looking at last (or only) element
19215  {
19216  ServiceList.push_back(InternalInput);
19217  InternalInput = "";
19218  }
19219  }
19220 
19221  ServiceList.sort(); // alphabetical order
19222  ServiceList.unique(); //remove duplicates
19223  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
19224  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19225  {
19226  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
19227  }
19228  if(Output.Length() > 0)
19229  {
19230  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
19231  }
19232  Utilities->CallLogPop(2217);
19233  return(Output);
19234 }
19235 
19236 // ---------------------------------------------------------------------------
19237 
19238 
19239 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
19240  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
19241 {
19242  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
19243  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
19244 
19245  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
19246  //general list pointers, LocPtrs point to Location in the two lists
19247 
19248  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
19249  //for List1
19250  bool LocFound = false;
19251  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
19252  int IncMinutes;
19253  TDateTime FirstServiceTime;
19254 
19255  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
19256  int Ref1Target = 0, Ref1Count = 0;
19257  int SlashPos = Ref1.Pos('/');
19258  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
19259  {
19260  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
19261  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
19262  }
19263  int Ref2Target = 0, Ref2Count = 0;
19264  SlashPos = Ref2.Pos('/');
19265  if(SlashPos > 0) //if 0 leave as is
19266  {
19267  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
19268  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
19269  }
19270  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
19271  {
19272  //even if others have same names. But if there are cdt's then need to refind the correct service
19273  if((*ListPtr1) == Location) //
19274  {
19275  LocPtr1 = ListPtr1; //may be modified later
19276  LocFound = true;
19277  }
19278  if(ListPtr1->SubString(1, 3) == "%%%")
19279  {
19280  AnsiString CDTTime = ListPtr1->SubString(4, 5);
19281  //now adjust the time to correspond to the repeat if there is one
19282  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
19283  {
19284  IncMinutes = -1;
19285  FirstServiceTime = TDateTime(-1);
19286  bool BreakFlag = false;
19287  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
19288  {
19289  if(TDVIt->ServiceReference == Ref1)
19290  {
19291  if(Ref1Target > Ref1Count)
19292  {
19293  Ref1Count++;
19294  continue;
19295  }
19296  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
19297  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
19298  {
19299  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
19300  {
19301  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
19302  BreakFlag = true;
19303  break;
19304  }
19305  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
19306  {
19307  FirstServiceTime = AVIt->ArrivalTime;
19308  BreakFlag = true;
19309  break;
19310  }
19311  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
19312  {
19313  FirstServiceTime = AVIt->DepartureTime;
19314  BreakFlag = true;
19315  break;
19316  }
19317  }
19318  if(BreakFlag)
19319  {
19320  break;
19321  }
19322  }
19323  }
19324  if(IncMinutes == -1)
19325  {
19326  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19327  }
19328  if(FirstServiceTime == TDateTime(-1))
19329  {
19330  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19331  }
19332  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
19333  }
19334  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
19335  {
19336  LocFound = false;
19337  continue;
19338  }
19339  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
19340  {
19341  break;
19342  }
19343  if(Time1 > CDTTime) //not there yet so go on
19344  {
19345  LocFound = false;
19346  continue;
19347  }
19348  if(Time1 < CDTTime) //gone too far so can stop now
19349  {
19350  break;
19351  }
19352  }
19353  }
19354  if(!LocFound) //have to find it in both lists
19355  {
19356  Utilities->CallLogPop(2228);
19357  return( false);
19358  }
19359  //for List2
19360  LocFound = false;
19361  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
19362  {
19363  if((*ListPtr2) == Location)
19364  {
19365  LocPtr2 = ListPtr2; //may be modified later
19366  LocFound = true;
19367  }
19368  if(ListPtr2->SubString(1, 3) == "%%%")
19369  {
19370  AnsiString CDTTime = ListPtr2->SubString(4, 5);
19371  //now adjust the time to correspond to the repeat if there is one
19372  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
19373  {
19374  IncMinutes = -1;
19375  FirstServiceTime = TDateTime(-1);
19376  bool BreakFlag = false;
19377  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
19378  {
19379  if(TDVIt->ServiceReference == Ref2)
19380  {
19381  if(Ref2Target > Ref2Count)
19382  {
19383  Ref2Count++;
19384  continue;
19385  }
19386  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
19387  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
19388  {
19389  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
19390  {
19391  FirstServiceTime = AVIt->EventTime;
19392  BreakFlag = true;
19393  break;
19394  }
19395  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
19396  {
19397  FirstServiceTime = AVIt->ArrivalTime;
19398  BreakFlag = true;
19399  break;
19400  }
19401  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
19402  {
19403  FirstServiceTime = AVIt->DepartureTime;
19404  BreakFlag = true;
19405  break;
19406  }
19407  }
19408  if(BreakFlag)
19409  {
19410  break;
19411  }
19412  }
19413  }
19414  if(IncMinutes == -1)
19415  {
19416  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19417  }
19418  if(FirstServiceTime == TDateTime(-1))
19419  {
19420  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19421  }
19422  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
19423  }
19424  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
19425  {
19426  LocFound = false;
19427  continue;
19428  }
19429  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
19430  {
19431  break;
19432  }
19433  if(Time2 > CDTTime) //not there yet so go on
19434  {
19435  LocFound = false;
19436  continue;
19437  }
19438  if(Time2 < CDTTime) //gone too far so can stop now
19439  {
19440  break;
19441  }
19442  }
19443  }
19444  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
19445  {
19446  Utilities->CallLogPop(2229);
19447  return( false);
19448  }
19449  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
19450  //set ListPtr1 to the search start position
19451  if(Arrival)
19452  {
19453  LP1 = List1.begin();
19454  LP1--; //now points to before the first entry
19455  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
19456  {
19457  if(ListPtr1 == List1.begin())
19458  {
19459  break;
19460  }
19461  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
19462  {
19463  ListPtr1++; //point to one past the cdt
19464  break;
19465  }
19466  }
19467  //set ListPtr2 to the search start position
19468  LP2 = List2.begin();
19469  LP2--; //now points to before the first entry
19470  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
19471  {
19472  if(ListPtr2 == List2.begin())
19473  {
19474  break;
19475  }
19476  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
19477  {
19478  ListPtr2++; //point to one past the cdt
19479  break;
19480  }
19481  }
19482  //ListPtr1 & 2 now at search start position
19483  LP1 = ListPtr1;
19484  LP2 = ListPtr2;
19485  //now search forwards, i.e. for common locations before Location
19486  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19487  {
19488  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
19489  {
19490  break;
19491  }
19492  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
19493  {
19494  break;
19495  }
19496  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19497  {
19498  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
19499  {
19500  break;
19501  }
19502  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
19503  {
19504  break;
19505  }
19506  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
19507  {
19508  Utilities->CallLogPop(2230);
19509  return( true);
19510  }
19511  }
19512  }
19513  }
19514 
19515  //now, for the departure analysis, reset the start positions and search locations after Location
19516 
19517  else
19518  {
19519  LP1 = LocPtr1;
19520  LP1++; //start at one past the location itself
19521  LP2 = LocPtr2;
19522  LP2++;
19523  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19524  {
19525  if(ListPtr1 == List1.end()) //reached end point so stop
19526  {
19527  break;
19528  }
19529  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
19530  {
19531  break;
19532  }
19533  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19534  {
19535  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
19536  {
19537  break;
19538  }
19539  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
19540  {
19541  break;
19542  }
19543  if((*ListPtr1) == (*ListPtr2)) //found a common later location
19544  {
19545  Utilities->CallLogPop(2231);
19546  return( true);
19547  }
19548  }
19549  }
19550  }
19551  Utilities->CallLogPop(2232);
19552  return( false);
19553 }
19554 
19555 // ---------------------------------------------------------------------------
19556 
19557 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
19558 {
19559  // changed at v2.7.0 to show allowable exit elements
19560  if(ExitList.empty())
19561  {
19562  return("");
19563  }
19564  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
19565  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
19566  AnsiString ExitLocList = "";
19567  AllowedExits = "";
19568 
19569  unsigned int Counter = 0;
19570  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
19571  {
19572  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
19573  Counter++;
19574  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
19575  {
19576  ExitLocList += "\n";
19577  }
19578  }
19579  if(StartName == "")
19580  {
19581  if(ExitList.size() == 1)
19582  {
19583  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
19584  Utilities->CallLogPop(1571);
19585  return(" at " + ID);
19586  }
19587  else
19588  {
19589  Utilities->CallLogPop(1572);
19590  if(ExitList.size() < 4)
19591  {
19592  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19593  return("");
19594  }
19595  else
19596  {
19597  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19598  return("");
19599  }
19600  }
19601  }
19602  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
19603  {
19604  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
19605  {
19606  Utilities->CallLogPop(1570);
19607  if(ExitList.size() < 4)
19608  {
19609  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19610  return("");
19611  }
19612  else
19613  {
19614  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19615  return("");
19616  }
19617  }
19618  }
19619  Utilities->CallLogPop(1569);
19620  if(ExitList.size() < 4)
19621  {
19622  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19623  return(" at " + StartName);
19624  }
19625  else
19626  {
19627  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19628  return(" at " + StartName);
19629  }
19630 }
19631 
19632 // ---------------------------------------------------------------------------
19633 /* can't trust this as locations within a vector may not be contiguous
19634  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
19635  {
19636  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
19637  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
19638  //must be preceded by a TimeLoc departure
19639  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
19640  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
19641  {
19642  if((AVPtr + x) < TDEPtr->ActionVector.end())
19643  {
19644  AnsiString xx = (AVPtr + x)->Command;//test
19645  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
19646  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
19647  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
19648  {
19649  Utilities->CallLogPop();
19650  return false;
19651  }
19652  else if((AVPtr + x)->SequenceType == Finish)
19653  {
19654  Utilities->CallLogPop();
19655  return true;
19656  }
19657  }
19658  }
19659  Utilities->CallLogPop();
19660  return false;
19661  }
19662 */
19663 // ---------------------------------------------------------------------------
19664 
19665 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
19666 {
19667  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
19668  AnsiString FormatStr = "####0.0";
19669  AnsiString AvLateArrMins = "";
19670  AnsiString AvEarlyArrMins = "";
19671  AnsiString AvLatePassMins = "";
19672  AnsiString AvEarlyPassMins = "";
19673  AnsiString AvLateDepMins = "";
19674  AnsiString AvLateExitMins = "";
19675  AnsiString AvEarlyExitMins = "";
19676 
19677  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
19678  for(unsigned int x = 0; x < TrainVector.size(); x++)
19679  {
19680  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
19681  }
19682 
19683  if(LateArrivals > 0)
19684  {
19685  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
19686  }
19687  if(EarlyArrivals > 0)
19688  {
19689  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
19690  }
19691  if(LatePasses > 0)
19692  {
19693  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
19694  }
19695  if(EarlyPasses > 0)
19696  {
19697  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
19698  }
19699  if(LateDeps > 0)
19700  {
19701  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
19702  }
19703  if(LateExits > 0) //added at v2.9.1
19704  {
19705  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
19706  }
19707  if(EarlyExits > 0) //added at v2.9.1
19708  {
19709  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
19710  }
19711  PerfFile << '\n' << '\n' << "***************************************";
19712  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
19713 
19714  if(OnTimeArrivals != 1)
19715  {
19716  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
19717  }
19718  else
19719  {
19720  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
19721  }
19722  if(LateArrivals > 1)
19723  {
19724  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
19725  }
19726  else if(LateArrivals == 1)
19727  {
19728  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
19729  }
19730  else
19731  {
19732  PerfFile << LateArrivals << " late arrivals" << '\n';
19733  }
19734  if(EarlyArrivals > 1)
19735  {
19736  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
19737  }
19738  else if(EarlyArrivals == 1)
19739  {
19740  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
19741  }
19742  else
19743  {
19744  PerfFile << EarlyArrivals << " early arrivals" << '\n';
19745  }
19746  if(OnTimePasses != 1)
19747  {
19748  PerfFile << OnTimePasses << " on-time passes" << '\n';
19749  }
19750  else
19751  {
19752  PerfFile << OnTimePasses << " on-time pass" << '\n';
19753  }
19754  if(LatePasses > 1)
19755  {
19756  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
19757  }
19758  else if(LatePasses == 1)
19759  {
19760  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
19761  }
19762  else
19763  {
19764  PerfFile << LatePasses << " late passes" << '\n';
19765  }
19766  if(EarlyPasses > 1)
19767  {
19768  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
19769  }
19770  else if(EarlyPasses == 1)
19771  {
19772  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
19773  }
19774  else
19775  {
19776  PerfFile << EarlyPasses << " early passes" << '\n';
19777  }
19778 
19779  if(OnTimeExits != 1) //this batch added at v2.9.1
19780  {
19781  PerfFile << OnTimeExits << " on-time exits" << '\n';
19782  }
19783  else
19784  {
19785  PerfFile << OnTimeExits << " on-time exit" << '\n';
19786  }
19787  if(LateExits > 1)
19788  {
19789  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
19790  }
19791  else if(LateExits == 1)
19792  {
19793  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
19794  }
19795  else
19796  {
19797  PerfFile << LateExits << " late exits" << '\n';
19798  }
19799  if(EarlyExits > 1)
19800  {
19801  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
19802  }
19803  else if(EarlyExits == 1)
19804  {
19805  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
19806  }
19807  else
19808  {
19809  PerfFile << EarlyExits << " early exits" << '\n';
19810  }
19811 
19812  if(OnTimeDeps != 1)
19813  {
19814  PerfFile << OnTimeDeps << " on-time departures" << '\n';
19815  }
19816  else
19817  {
19818  PerfFile << OnTimeDeps << " on-time departure" << '\n';
19819  }
19820  if(LateDeps > 1)
19821  {
19822  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
19823  }
19824  else if(LateDeps == 1)
19825  {
19826  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
19827  }
19828  else
19829  {
19830  PerfFile << LateDeps << " late departures" << '\n';
19831  }
19832  TDateTime TempExcessLCDownTime;
19833  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
19834  {
19835 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
19836  //later perf summaries with lower values, changed at v2.8.0
19837 // {
19838  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
19839 // }
19840 /*
19841  else
19842  {
19843  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
19844  }
19845 */
19846  if(TempExcessLCDownTime > TDateTime(0))
19847  {
19848  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
19849  }
19850  }
19851 
19852  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
19853 
19854  if(ExcessLCDownMins > 0.1)
19855  {
19856  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
19857  }
19858  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
19859  {
19860  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
19861  }
19862  if(MissedStops != 1)
19863  {
19864  PerfFile << MissedStops << " missed stops" << '\n';
19865  }
19866  else
19867  {
19868  PerfFile << MissedStops << " missed stop" << '\n';
19869  }
19870  if(OtherMissedEvents != 1)
19871  {
19872  PerfFile << OtherMissedEvents << " other missed events" << '\n';
19873  }
19874  else
19875  {
19876  PerfFile << OtherMissedEvents << " other missed event" << '\n';
19877  }
19878  if(SkippedTTEvents != 1)
19879  {
19880  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
19881  }
19882  else
19883  {
19884  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
19885  }
19886  if(UnexpectedExits != 1)
19887  {
19888  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
19889  }
19890  else
19891  {
19892  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
19893  }
19894  if(IncorrectExits != 1)
19895  {
19896  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
19897  }
19898  else
19899  {
19900  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
19901  }
19902  if(NumFailures != 1)
19903  {
19904  PerfFile << NumFailures << " train failures" << '\n';
19905  }
19906  else
19907  {
19908  PerfFile << NumFailures << " train failure" << '\n';
19909  }
19910  if(AvHoursIntValue > 0)
19911  {
19912  if(AvHoursIntValue == 1)
19913  {
19914  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
19915  }
19916  else
19917  {
19918  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
19919  }
19920  }
19921  AnsiString AvLateMinsLocsNotReached = "";
19922 
19924  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
19925  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
19926 
19927  if(LocsNotReached > 0)
19928  {
19929  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
19930  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
19931  }
19932  if(SPADRisks != 1)
19933  {
19934  PerfFile << SPADRisks << " SPAD risks" << '\n';
19935  }
19936  else
19937  {
19938  PerfFile << SPADRisks << " SPAD risk" << '\n';
19939  }
19940  if(SPADEvents != 1)
19941  {
19942  PerfFile << SPADEvents << " SPADs" << '\n';
19943  }
19944  else
19945  {
19946  PerfFile << SPADEvents << " SPAD" << '\n';
19947  }
19948  if(Derailments != 1)
19949  {
19950  PerfFile << Derailments << " derailments" << '\n';
19951  }
19952  else
19953  {
19954  PerfFile << Derailments << " derailment" << '\n';
19955  }
19956  if(CrashedTrains != 1)
19957  {
19958  PerfFile << CrashedTrains << " crashed trains" << '\n';
19959  }
19960  else
19961  {
19962  PerfFile << CrashedTrains << " crashed train" << '\n';
19963  }
19964  PerfFile << '\n' << "***************************************" << '\n';
19965 
19966  bool DerailSPADFlag = false, CrashFlag = false;
19967 
19968  int OverallScorePercent = 100;
19969  int TotArrDepExit = 0;
19970  double TotLateMinsFactor = 1;
19971  double MissedStopAndSPADRiskFactor = 1;
19972  double NetNegFactor = 1;
19973 
19975  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
19976  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
19977  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
19978  // 'no timetabled departures... message, which was inappropriate
19979 
19980  if((SPADEvents > 0) || (Derailments > 0))
19981  {
19982  OverallScorePercent = 5; // overrides other calculations
19983  DerailSPADFlag = true;
19984  }
19985  if(CrashedTrains > 0)
19986  {
19987  OverallScorePercent = 0; // overrides other calculations
19988  CrashFlag = true;
19989  }
19990  if(OverallScorePercent == 100)
19991  {
19992  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
19993  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
19994  {
19995  LatenessPenalty = 0;
19996  }
19997  else
19998  {
19999  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
20000  }
20001  if(TotArrDepExit > 0)
20002  {
20003  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
20004  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
20005  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
20006  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
20007  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
20008  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
20009  // of arrivals & departures, where 4% = half, 8% = a quarter etc
20010  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
20011  // NetNegfactor: product of the above two
20012  OverallScorePercent = 100 * NetNegFactor;
20013  }
20014  }
20015  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
20016  // flag condits added at v1.1.4 - see above for what the error was
20017  {
20018  AnsiString OneFailureString = ", though the failure would account for some poor performance";
20019  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
20020  AnsiString AddedString = "";
20021  if(NumFailures == 1)
20022  {
20023  AddedString = OneFailureString;
20024  }
20025  if(NumFailures > 1)
20026  {
20027  AddedString = TwoOrMoreFailureString;
20028  }
20029  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
20030  AnsiString Rating = "";
20031  if(OverallScorePercent == 100)
20032  {
20033  Rating = "Perfect!";
20034  }
20035  else if(OverallScorePercent >= 95)
20036  {
20037  Rating = "Excellent";
20038  }
20039  else if(OverallScorePercent >= 90)
20040  {
20041  Rating = "Very good";
20042  }
20043  else if(OverallScorePercent >= 80)
20044  {
20045  Rating = "Good";
20046  }
20047  else if(OverallScorePercent >= 70)
20048  {
20049  Rating = "Fair";
20050  }
20051  else if(OverallScorePercent >= 60)
20052  {
20053  Rating = "Unacceptable" + AddedString;
20054  }
20055  else if(OverallScorePercent >= 50)
20056  {
20057  Rating = "Poor" + AddedString;
20058  }
20059  else if(OverallScorePercent >= 40)
20060  {
20061  Rating = "Bad" + AddedString;
20062  }
20063  else if(OverallScorePercent >= 30)
20064  {
20065  Rating = "Very bad" + AddedString;
20066  }
20067  else if(OverallScorePercent >= 20)
20068  {
20069  Rating = "Terrible" + AddedString;
20070  }
20071  else if(OverallScorePercent >= 10)
20072  {
20073  Rating = "Appalling" + AddedString;
20074  }
20075  else if(OverallScorePercent >= 5)
20076  {
20077  if(DerailSPADFlag)
20078  {
20079  Rating = "Disastrous - potential loss of life";
20080  }
20081  // SPADs/Derailments
20082  else
20083  {
20084  Rating = "Dire" + AddedString;
20085  }
20086  }
20087  else if(OverallScorePercent < 5)
20088  {
20089  if(CrashFlag)
20090  {
20091  Rating = "Catastrophic - loss of life"; // Crashes
20092  }
20093  else
20094  {
20095  Rating = "Abysmal";
20096  }
20097  }
20098  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
20099  }
20100  else
20101  {
20102  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
20103  }
20104  PerfFile << '\n' << "***************************************";
20105  PerfFile.flush();
20106  Utilities->CallLogPop(1736);
20107 }
20108 
20109 // ---------------------------------------------------------------------------
20110 
20112 {
20113  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
20114  for(unsigned int x = 0; x < TrainVector.size(); x++)
20115  {
20116  TTrain &Train = TrainVectorAt(58, x);
20117  if(Train.Crashed)
20118  // can't use background colours for crashed & derailed because same colour
20119  {
20120  CrashWarning = true;
20121  }
20122  else if(Train.Derailed)
20123  // can't use background colours for crashed & derailed because same colour
20124  {
20125  DerailWarning = true;
20126  }
20127  else if(Train.BackgroundColour == clSPADBackground)
20128  // use colour as that changes as soon as passes signal
20129  {
20130  SPADWarning = true;
20131  }
20132  else if(Train.BackgroundColour == clTrainFailedBackground)
20133  {
20134  TrainFailedWarning = true;
20135  }
20136  else if(Train.BackgroundColour == clCallOnBackground)
20137  // use colour as also stopped at signal
20138  {
20139  CallOnWarning = true;
20140  }
20141  else if(Train.BackgroundColour == clSignalStopBackground)
20142  // use colour to distinguish from call-on
20143  {
20144  SignalStopWarning = true;
20145  }
20146  else if(Train.BackgroundColour == clBufferAttentionNeeded)
20147  // use colour to distinguish from ordinary buffer stop
20148  {
20149  BufferAttentionWarning = true;
20150  }
20151  }
20152  Utilities->CallLogPop(1796);
20153 }
20154 
20155 // ---------------------------------------------------------------------------
20156 
20158 {
20159  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
20160 
20161  // calculate lateness for running trains
20164  for(unsigned int x = 0; x < TrainVector.size(); x++)
20165  {
20166  TTrain &Train = TrainVectorAt(64, x);
20167  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
20168  AVEntryPtr++)
20169  {
20170  if(AVEntryPtr < Train.ActionVectorEntryPtr)
20171  {
20172  continue;
20173  }
20174  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
20175  TTClockTime))
20176  {
20177  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
20179  }
20180 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
20181  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
20182  TTClockTime))
20183  {
20184  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
20185  OperatingTrainArrDep++;
20186  }
20187 */
20188  }
20189  }
20190 
20191  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
20194 
20195  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
20196  {
20197  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
20198  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
20199  int IncrementalMinutes = 0;
20200  if(AVEntryLast.FormatType == Repeat)
20201  {
20202  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
20203  }
20204  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
20205  {
20206  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
20207  if(TTOD.RunningEntry != NotStarted)
20208  {
20209  continue;
20210  }
20211  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
20212  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
20213  bool TrainOperatingFlag = false;
20214  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
20215  {
20216  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
20217  {
20218  TrainOperatingFlag = true;
20219  break;
20220  }
20221  }
20222  if(TrainOperatingFlag)
20223  {
20224  continue;
20225  }
20226  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
20227  {
20228  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
20229  }
20230  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
20231  {
20232  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
20233  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
20234  {
20235  break; // all the rest will also be greater (& default of -1 will be less)
20236  }
20237  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
20238  {
20239  break; // all the rest will also be greater (& default of -1 will be less)
20240  }
20241  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
20242  {
20243  break; // all the rest will also be greater (& default of -1 will be less)
20244  }
20245  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
20246  {
20247  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
20249  }
20250 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
20251  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
20252  {
20253  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
20254  NotStartedTrainArrDep++;
20255  }
20256 */
20257  }
20258  }
20259  }
20260  Utilities->CallLogPop(1894);
20261 }
20262 
20263 // ---------------------------------------------------------------------------
20264 
20266 // new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2.13.0)
20267 // clears entries then adds values for running trains then for continuation entries
20268 // dont limit size here as need to check all trains (ActionsDueListBox is limited to 20 trains in Interface.cpp)
20269 {
20270  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
20271  OpTimeToActMultiMap.clear();
20272  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
20273 
20274  if(!TrainVector.empty())
20275  // build OpTimeToActMultiMap entries for running trains
20276  {
20277  AnsiString HeadCode;
20278  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
20279  int TrainID;
20280  THCandTrainPosParam HCandTrainPosParam;
20281  for(unsigned int x = 0; x < TrainVector.size(); x++)
20282  {
20283  HeadCode = TrainVectorAt(62, x).HeadCode;
20284  TrainID = TrainVectorAt(63, x).TrainID;
20285  HCandTrainPosParam.first = HeadCode;
20286  HCandTrainPosParam.second = TrainID;
20287  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
20288  if((TimeToAct >= 0) && (TimeToAct < 59.9))
20289  // -1 indicates don't display
20290  {
20291  OpTimeToActMultiMapEntry.first = TimeToAct;
20292  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
20293  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
20294  }
20295  }
20296  }
20297 /*
20298  * class TContinuationTrainExpectationEntry
20299  {
20300  public:
20301  AnsiString Description; ///< service description
20302  AnsiString HeadCode; ///< service headcode
20303  int RepeatNumber; ///< service RepeatNumber
20304  int IncrementalMinutes; ///< Repeat separation in minutes
20305  int IncrementalDigits; ///< Repeat headcode separation
20306  int VectorPosition; ///< TrackVectorPosition for the continuation element
20307  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
20308  };
20309 
20310  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
20311  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
20312  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
20313  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
20314 */
20315 
20317  // build OpTimeToActMultiMap entries for expected trains
20318  {
20319  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
20320  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
20321  float TimeToAct = 0; // minutes
20322  int DistanceToRedSignal = 0; // metres
20323  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
20324  // used to ensure only one train displayed for a given continuation
20325  ContinuationEntryVecPosVector.clear();
20326  bool LaterTrain = false;
20329  {
20330  LaterTrain = false;
20331  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
20332  {
20333  CTEIt++;
20334  continue; // not interested in running or exited trains
20335  }
20336  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
20337  {
20338  CTEIt++;
20339  continue;
20340  // don't include trains not entered yet when a train is already on the continuation
20341  }
20342  if(!ContinuationEntryVecPosVector.empty())
20343  {
20344  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
20345  {
20346  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
20347  {
20348  LaterTrain = true;
20349  ;
20350  // skip past remaining trains waiting to enter at same point
20351  break;
20352  }
20353  }
20354  }
20355  if(LaterTrain)
20356  {
20357  CTEIt++;
20358  continue;
20359  }
20360  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
20361  AnsiString HeadCode = CTEIt->second.HeadCode;
20362  float CurrentStopTime; // set to 0 at start of function
20363  float LaterStopTime; // set to 0 at start of function
20364  float RecoverableTime; // set to 0 at start of function
20365  int AvTrackSpeed; // set to 0 at start of function
20366  int TrainID = -1; // not yet allocated for train still to enter
20367  int DistanceToExit; //not used for continuation entries
20368  THVShortPair ExitPair;
20369  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
20370 
20371 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
20372 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
20373 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
20374 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
20375 
20376  int AtValue = 1;
20377  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
20378  {
20379  AtValue = 0;
20380  }
20381  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
20382  // EntryPos always 0 for entering at a continuation
20383  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
20384  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
20385  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
20386  // for a train it's the one in front of LeadElement
20387  if(AvTrackSpeed < 30)
20388  {
20389  AvTrackSpeed = 30;
20390  }
20391  if(DistanceToRedSignal == -1)
20392  {
20393  TimeToAct = 60.0;
20394  }
20395  else
20396  {
20397  int Speed = AvTrackSpeed;
20398  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
20399  if(AvTrackSpeed > MaxSpeed)
20400  {
20401  Speed = MaxSpeed;
20402  }
20403  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
20404  // defined in timetable as under signaller control
20405  {
20406  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
20407  LaterStopTime = 0;
20408  }
20409  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
20410  // accel & decel taken into account in
20411  // CalcDistanceToRedSignalandStopTime
20412  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
20413  // don't need CurrentStopTime or RecoverableTime for continuation entries
20414  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
20415  TimeToAct += MinsBefEnter;
20416  }
20417  THCandTrainPosParam HCandTrainPosParam;
20418  HCandTrainPosParam.first = HeadCode;
20419  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
20420  // -1-CTE... because 2nd value covers TrainID if +ve &
20421  // continuation track vector position if -ve, -1 allows for vecpos being 0
20422  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
20423  {
20424  OpTimeToActMultiMapEntry.first = TimeToAct;
20425  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
20426  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
20427  }
20428  CTEIt++;
20429  }
20430  }
20431  Utilities->CallLogPop(2081);
20432 }
20433 
20434 // ---------------------------------------------------------------------------
20435 
20437 // new for multiplayer
20438 // clears entries then adds values for running trains
20439 {
20440  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
20441  TimeToExitMultiMap.clear();
20442  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
20443 
20444  if(!TrainVector.empty())
20445  // build map entries for running trains
20446  {
20447  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
20448  THVShortPair ExitPair;
20449  float TimeToExit;
20450  for(unsigned int x = 0; x < TrainVector.size(); x++)
20451  {
20453  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
20454  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
20455  ExitPair = TrainVectorAt(76, x).ExitPair;
20456  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
20457  {
20458  ExitInfo.TimeToExitSecs = -1;
20459  }
20460  TimeToExitMultiMapEntry.first = ExitPair;
20461  TimeToExitMultiMapEntry.second = ExitInfo;
20462  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
20463  }
20464  }
20465  Utilities->CallLogPop(2323);
20466 }
20467 
20468 // ---------------------------------------------------------------------------
20469 
20470 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
20471  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
20472  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
20473 // new v2.2.0
20474 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
20475 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
20476 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
20477 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
20478 // aren't used - this means there is no display for the train in question
20479 {
20480  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
20481  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
20482  int DistanceToRedSignal = 0;
20483  DistanceToExit = -1;
20484  ExitPair.first = -1;
20485  ExitPair.second = -1;
20486  int CumTrackSpeed = 0;
20487  // average track speed, in case need to use in time calc
20488  int TrackSpeedCount = 0;
20489  float KmPerLocationStop;
20490  float MaxAllowableSpeed;
20491 
20492  //below added at v2.6.1
20493  if(TrainID > -1)
20494  {
20495  TTrain &Train = TrainVectorAtIdent(51, TrainID);
20496  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
20497  Train.StationStopCalculated = false;
20498  }
20499  AvTrackSpeed = 0;
20500  int CurrentElement = TrackVectorPosition;
20501  int CurrentEntryPos = TrackVectorPositionEntryPos;
20502  int NextElement;
20503  int NextEntryPos;
20504  int NextExitPos;
20505 
20506  CurrentStopTime = 0;
20507  LaterStopTime = 0;
20508  RecoverableTime = 0;
20509  if(CurrentElement == -1) // end element, no action needed
20510  {
20511  Utilities->CallLogPop(2094);
20512  return(-1);
20513  }
20514  int CurrentExitPos;
20515 
20516  // get ExitPos for first element to be measured
20517  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
20518  {
20519  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
20520  {
20521  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
20522  {
20523  CurrentExitPos = 1;
20524  }
20525  else
20526  {
20527  CurrentExitPos = 3;
20528  }
20529  }
20530  else
20531  {
20532  CurrentExitPos = 0; // trailing point
20533  }
20534  }
20535  else
20536  {
20537  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
20538  }
20539  // get CumTrackSpeed for first measured element
20540 
20541  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
20542  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
20543 
20544  // check if currently stopped at a location, and if so add the remaining dwell time
20545  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
20546  if(TrainID > -1)
20547  // -1 for a continuation and can't be at a location as not yet entered
20548  {
20549  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
20551  // this used to deduct from RecoverableTime when arrive at a location
20552  if(Train.RevisedStoppedAtLoc())
20553  {
20554  if(Train.StoppedForTrainInFront)
20555  {
20556  Utilities->CallLogPop(2082);
20557  return(-1); // no action needed
20558  }
20559  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
20560  {
20561  Utilities->CallLogPop(2083);
20562  return(-1); // not due a departure so no action needed
20563  }
20564  else // due a departure
20565  {
20566  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
20567  // can't convert a TDateTime to a float directly
20568  CurrentStopTime = float(TimeToDepart);
20569  AVPtr++;
20570  }
20571  }
20572  }
20573  // check if CurrentElement is a red signal, but ok if autosig route after
20574  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20575  // ok if autosig route after red signal
20576  {
20577  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
20578  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
20579  int RouteNumber; // holder for referenced value, not used
20580  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20581  {
20582  Utilities->CallLogPop(2078);
20583  return(-1);
20584  }
20585  else if(SigControlAndCanPassRedSignal)
20586  // ignore signal and increment CurrentElement to NextElement
20587  {
20588  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
20589  {
20590  if((NextEntryPos == 0) || (NextEntryPos == 2))
20591  // leading entry point
20592  {
20593  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
20594  {
20595  NextExitPos = 1;
20596  }
20597  else
20598  {
20599  NextExitPos = 3;
20600  }
20601  }
20602  else
20603  {
20604  NextExitPos = 0; // trailing entry point
20605  }
20606  }
20607  else
20608  {
20609  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20610  }
20611  CurrentElement = NextElement;
20612  CurrentEntryPos = NextEntryPos;
20613  CurrentExitPos = NextExitPos;
20614  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
20615  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
20616  }
20617  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
20618  // give 'NOW' indication after allowed to pass stop signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
20619  {
20620  Utilities->CallLogPop(2084);
20621  return(0);
20622  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
20623  }
20624  }
20625  int LaterStopNumber = 0;
20626  int x = 0;
20627  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
20628 
20629  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
20630  // not red signal next (in fwd direction) so enter loop to calc CumLength
20631  {
20632  x++; // added in v2.4.0 as above
20633  if(x > 5000)
20634  {
20635  Utilities->CallLogPop(2120);
20636  return(-1);
20637  }
20638  if(CurrentEntryPos > 1)
20639  {
20640  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
20641  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
20642  }
20643  else
20644  {
20645  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
20646  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
20647  }
20648  TrackSpeedCount++;
20649 
20650  //added for multiplayer - exiting at a continuation and continuation length already added
20651  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
20652  {
20653  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
20654  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
20655  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
20656  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
20657  //need here as next element will be -1 so will exit before calcs at end
20658  if(TrackSpeedCount > 0)
20659  {
20660  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20661  }
20662  else // shouldn't reach here but include to prevent divide by zero error
20663  {
20664  if(CurrentEntryPos > 1)
20665  {
20666  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
20667  }
20668  else
20669  {
20670  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
20671  }
20672  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20673  }
20674  //calc AvTrackSpeed
20675  if(LaterStopNumber > 0)
20676  {
20677  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20678  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20679  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20680  // average line speed/2 (for half distance accelerating and half decelerating.
20681  }
20682  else
20683  {
20684  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20685  // using linear trendline for accel & decel distance at various speeds
20686  // at half braking, speed never < 60 using this
20687  }
20688  if(AvTrackSpeed > MaxAllowableSpeed)
20689  {
20690  AvTrackSpeed = MaxAllowableSpeed;
20691  }
20692  }
20693 
20694  // added at v2.6.1 to find DistanceToStationStop for trains running early
20695  if(TrainID > -1) //can ignore continuation entries as these don't run early
20696  {
20697  TTrain &Train = TrainVectorAtIdent(52, TrainID);
20698  if(!Train.StationStopCalculated)
20699  {
20700  if(Train.TrainMode == Timetable)
20701  {
20702  bool StopRequired = false;
20703  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
20704  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
20705  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
20706  {
20707  // no need to add in the length of element to CumulativeLength
20708  if(StopRequired)
20709  {
20710  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
20711  Train.StationStopCalculated = true; //don't want to update it with later stops
20712  }
20713  }
20714  }
20715  }
20716  }
20717  // check for train in front, but if on a bridge on other track then ok
20718  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
20719  int TrainOnElement;
20720  if(TE.TrackType != Bridge)
20721  {
20722  TrainOnElement = TE.TrainIDOnElement;
20723  }
20724  else
20725  {
20726  if(CurrentEntryPos > 1)
20727  {
20729  }
20730  else
20731  {
20733  }
20734  }
20735  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
20736  // train in front before red signal
20737  {
20738  Utilities->CallLogPop(2085);
20739  return(-1);
20740  }
20741  // add to stoptime if required
20742  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
20743  {
20744  double StopTimeDouble;
20745  while(AVPtr->FormatType == PassTime)
20746  {
20747  AVPtr++; // skip past any passes
20748  }
20749  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
20750  (AVPtr->FormatType == TimeTimeLoc)))
20751  // stop due here so calc dwell time & advance Ptr
20752  {
20753  if(AVPtr->FormatType == TimeTimeLoc)
20754  {
20755  LaterStopNumber++;
20756  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20757  if(StopTimeDouble < 0.5)
20758  {
20759  StopTimeDouble = 0.5;
20760  }
20761  // at least 30 secs delay at station
20762  // can't convert a TDateTime to a float directly
20763  LaterStopTime += float(StopTimeDouble);
20764  RecoverableTime += StopTimeDouble - 0.5;
20765  if((LaterStopNumber == 1) && (TrainID > -1))
20766  {
20767  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20768  }
20769  AVPtr++;
20770  }
20771  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
20772  {
20773  if((AVPtr + 1)->FormatType == TimeLoc)
20774  // must be a departure
20775  {
20776  LaterStopNumber++;
20777  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20778  // can't convert a TDateTime to a float directly
20779  if(StopTimeDouble < 0.5)
20780  {
20781  StopTimeDouble = 0.5;
20782  }
20783  // at least 30 secs delay at station
20784  LaterStopTime += float(StopTimeDouble);
20785  RecoverableTime += StopTimeDouble - 0.5;
20786  if((LaterStopNumber == 1) && (TrainID > -1))
20787  {
20788  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20789  }
20790  AVPtr++;
20791  AVPtr++;
20792  }
20793  else // not a departure, does something else at the location so no calculation needed
20794  {
20795  Utilities->CallLogPop(2086);
20796  return(-1);
20797  }
20798  }
20799  }
20800  }
20801  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
20802  if(NextElement == -1) // reached end element, no action needed
20803  {
20804  Utilities->CallLogPop(2077);
20805  return(-1);
20806  }
20807  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
20808  // get NextExitPos
20809  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
20810  {
20811  if((NextEntryPos == 0) || (NextEntryPos == 2))
20812  // leading entry point
20813  {
20814  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
20815  {
20816  NextExitPos = 1;
20817  }
20818  else
20819  {
20820  NextExitPos = 3;
20821  }
20822  }
20823  else
20824  {
20825  NextExitPos = 0; // trailing entry point
20826  }
20827  }
20828  else
20829  {
20830  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20831  }
20832  CurrentElement = NextElement;
20833  CurrentEntryPos = NextEntryPos;
20834  CurrentExitPos = NextExitPos;
20835  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
20836  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
20837  }
20838  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20839  // ok if autosig route after red signal, no action needed
20840  {
20841  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
20842  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
20843  int RouteNumber; // holder for referenced value, not used
20844  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20845  {
20846  Utilities->CallLogPop(2095);
20847  return(-1);
20848  }
20849  }
20850 
20851  if(TrackSpeedCount > 0)
20852  {
20853  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20854  }
20855  else // shouldn't reach here but include to prevent divide by zero error
20856  {
20857  if(CurrentEntryPos > 1)
20858  {
20859  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
20860  }
20861  else
20862  {
20863  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
20864  }
20865  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20866  }
20867 
20868  if(LaterStopNumber > 0)
20869  {
20870  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20871  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20872  }
20873  else
20874  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20875  // average line speed/2 (for half distance accelerating and half decelerating.
20876  {
20877  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20878  // using linear trendline for accel & decel distance at various speeds
20879  // at half braking, speed never < 60 using this
20880  }
20881  if(AvTrackSpeed > MaxAllowableSpeed)
20882  {
20883  AvTrackSpeed = MaxAllowableSpeed;
20884  }
20885  Utilities->CallLogPop(2096);
20886  return(DistanceToRedSignal);
20887 }
20888 
20889 // ---------------------------------------------------------------------------
20890 // end of TTrainController entries
20891 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8983
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:18318
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:136
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:378
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:52
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:15041
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:345
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:344
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:487
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:699
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:72
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1731
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:901
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:174
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:15064
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:14709
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6421
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:366
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:20265
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:807
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:16106
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:91
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:53
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:1014
Create
@ Create
Definition: TrainUnit.h:52
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:786
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:19324
Arrive
@ Arrive
Definition: TrainUnit.h:52
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:52
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:343
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:384
Depart
@ Depart
Definition: TrainUnit.h:52
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:718
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11895
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:716
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:428
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:804
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:284
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7305
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1648
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:53
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:907
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1660
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:726
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:142
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:786
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1672
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:66
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:504
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:747
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:432
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:20073
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13987
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:152
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:366
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:1000
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:412
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:983
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:793
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:339
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:979
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:19557
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7859
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:386
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:478
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:366
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6544
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:793
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:10260
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:320
THVShortPair
std::pair< short, short > THVShortPair
Definition: InterfaceUnit.h:81
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:898
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route
Definition: TrainUnit.h:721
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:15837
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:110
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:851
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:1006
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:1013
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:19114
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:793
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10451
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:11848
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1652
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8955
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:97
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:136
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:966
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:743
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:15852
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:265
Intermediate
@ Intermediate
Definition: TrainUnit.h:77
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:964
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6350
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:980
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3593
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:681
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:500
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:460
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:10222
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:54
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:493
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10882
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:845
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:789
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:346
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:721
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:372
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:207
LeadMid
@ LeadMid
Definition: TrainUnit.h:298
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:835
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:19588
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:14789
TTrack::ResetAllTrainIDsAndFailedPointOrigSpeedLimits
void ResetAllTrainIDsAndFailedPointOrigSpeedLimits(int Caller)
Definition: TrackUnit.cpp:7677
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:20436
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:978
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6565
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1550
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:739
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1020
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:252
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:1016
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:215
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:855
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:812
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:729
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:327
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9426
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15955
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:298
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:892
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:15893
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:52
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:298
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:808
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:347
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:53
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:448
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:728
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:995
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18862
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11456
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:989
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5860
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:507
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:186
TTrain
Definition: TrainUnit.h:304
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:986
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11475
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11618
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:887
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:250
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:869
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:484
GapJump
@ GapJump
Definition: TrackUnit.h:65
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:1007
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:838
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:150
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:150
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:408
Finish
@ Finish
Definition: TrainUnit.h:77
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6301
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9853
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:366
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1699
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:366
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:360
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:225
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:702
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:364
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:63
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:15934
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:831
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:323
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:784
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:464
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:109
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:717
TTrain::LastSigPassedFailed
bool LastSigPassedFailed
flag used to erase route elements in an autosigs route after a failed signal
Definition: TrainUnit.h:394
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:15657
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1410
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6131
End
@ End
Definition: TrackUnit.h:75
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:66
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:398
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7163
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8854
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:341
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10607
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:302
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:404
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:16017
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:124
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:974
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1024
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:843
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:209
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:832
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:422
SignalPost
@ SignalPost
Definition: TrackUnit.h:65
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:968
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:468
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1676
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:349
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:12536
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:996
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:209
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5629
TUtilities::MinorDelayFactor
float MinorDelayFactor
Definition: Utilities.h:47
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:150
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:846
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:770
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:896
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:420
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:74
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:480
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:64
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:841
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:807
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:895
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1887
TExitInfo
Definition: TrainUnit.h:106
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TUtilities::ModerateDelayCutoff
float ModerateDelayCutoff
Definition: Utilities.h:45
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:484
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:839
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:984
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:93
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:136
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:484
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:16814
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:962
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:458
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:987
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:10793
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:741
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:366
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:456
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:1012
Pass
@ Pass
Definition: TrainUnit.h:54
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:374
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:93
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:793
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:755
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3219
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11330
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:823
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:999
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:491
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:773
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:877
TUtilities::ModerateDelayFactor
float ModerateDelayFactor
Definition: Utilities.h:48
TUtilities::MajorDelayCutoff
float MajorDelayCutoff
Definition: Utilities.h:46
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:972
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:715
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:354
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:317
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:1005
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:818
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8967
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:1008
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:211
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:221
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:7015
TTrainController::MTBFHours
double MTBFHours
Mean time between train failures in timetable clock hours.
Definition: TrainUnit.h:810
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:711
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17947
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:472
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:15326
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10748
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:311
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3427
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1019
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:466
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5219
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2740
RearSplit
@ RearSplit
Definition: TrainUnit.h:52
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:134
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:54
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:828
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:665
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:969
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:816
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:992
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:134
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:55
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10642
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3151
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6987
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:15424
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:54
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:894
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:14087
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:837
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:861
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:380
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3543
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:965
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9031
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:85
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:331
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:710
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8777
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:8056
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3143
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:822
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:487
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time & next location
Definition: TrainUnit.cpp:7547
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:462
NewService
@ NewService
Definition: TrainUnit.h:52
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:16170
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1512
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:484
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:382
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1659
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:370
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:223
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:219
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:829
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:91
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:1009
Crossover
@ Crossover
Definition: TrackUnit.h:65
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4819
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:735
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:75
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:971
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:1002
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:796
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:18555
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:875
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:19197
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1660
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:464
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10593
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:329
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:15868
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8343
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:18381
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1021
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2358
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:484
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1681
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:961
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:484
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:753
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:313
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:795
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:52
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:430
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:807
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:991
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:967
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:366
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:440
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:1003
Leave
@ Leave
Definition: TrainUnit.h:52
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:848
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:765
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:791
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel (OperatorA...
Definition: TrainUnit.h:454
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:794
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:396
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3124
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:970
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:803
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2669
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:452
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:484
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3572
Enter
@ Enter
Definition: TrainUnit.h:52
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:515
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7892
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:977
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:15917
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:446
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:276
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:14617
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:745
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6500
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9461
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14996
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:7072
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:801
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11181
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:786
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:132
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:18225
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:105
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:793
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:272
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:20157
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:436
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:763
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:263
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:799
Terminate
@ Terminate
Definition: TrainUnit.h:52
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:807
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:800
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:484
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:148
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:498
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:368
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:285
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3669
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:896
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13959
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:15436
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:16193
Nil
@ Nil
Definition: Utilities.h:37
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:456
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11401
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:724
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:239
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:849
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:424
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2555
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:19239
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:865
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1646
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9942
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9514
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6914
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:859
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:994
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:127
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:517
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:107
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9525
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:1011
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:470
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1733
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:807
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11936
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3623
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3323
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:464
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:20470
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:808
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:54
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1644
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8924
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3187
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:122
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:825
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:16039
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:266
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:988
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:15403
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:146
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:867
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:485
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4756
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:278
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:10276
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:53
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:356
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:406
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:12036
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:821
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:96
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9539
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points & TSRs.
Definition: TrackUnit.h:140
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:154
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:844
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:903
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:444
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:747
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1714
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:213
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:129
TUtilities::MinorDelayCutoff
float MinorDelayCutoff
Definition: Utilities.h:44
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:116
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:993
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:227
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:438
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:148
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:814
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:910
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:558
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5242
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:466
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1650
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:97
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1660
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:140
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:335
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:1017
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:708
Moderate
@ Moderate
Definition: Utilities.h:37
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:706
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9926
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:894
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:830
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:352
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:16206
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:766
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:982
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:152
Major
@ Major
Definition: Utilities.h:37
Points
@ Points
Definition: TrackUnit.h:65
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:11528
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:740
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:392
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:824
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:14516
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:390
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:807
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:847
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:54
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3517
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:1001
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:400
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:502
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:17332
Continuation
@ Continuation
Definition: TrackUnit.h:65
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:793
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:466
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7650
GraphicUnit.h
PerfLogUnit.h
TUtilities::MajorDelayFactor
float MajorDelayFactor
Definition: Utilities.h:49
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:764
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:840
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:476
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10665
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:55
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:362
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:19665
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:712
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3281
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:899
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:54
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:776
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:902
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:1015
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10307
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:87
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:325
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:482
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:675
Minor
@ Minor
Definition: Utilities.h:37
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:738
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6747
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:138
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:842
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:489
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:266
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:509
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:54
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:998
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19903
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:515
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:18919
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:904
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:990
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:11039
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:315
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:285
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:706
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:18812
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:713
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:717
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8887
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:793
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:320
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:762
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:187
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:723
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:985
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:19012
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18968
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:442
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:797
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:124
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:282
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:154
Start
@ Start
Definition: TrainUnit.h:77
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:906
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:333
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:51
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:717
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:366
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:834
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1022
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9893
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:15350
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7631
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:18238
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6804
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:416
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5682
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:18955
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9911
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:148
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13966
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:342
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15999
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:185
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:65
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:933
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:388
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:217
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:836
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:12384
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:99
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:766
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:807
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:176
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:198
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:857
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:828
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:805
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:522
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5326
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:484
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:10295
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:981
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:142
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:297
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:376
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:778
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:108
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:240
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:15030
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7389
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:450
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:704
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1654
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:144
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:358
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:768
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:975
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:1018
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:764
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3245
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled event entries, null on creation
Definition: TrainUnit.h:124
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:146
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:410
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:95
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:1010
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:124
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6849
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4951
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:737
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:150
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:402
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:853
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:807
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:973
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6413
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:725
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:53
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:767
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:20111
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:733
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:820
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:1004
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:871
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:719
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:128
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5231
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:434
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:79
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:150
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:723
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:337
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:752
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:729
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:833
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3169
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:89
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:650
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1071
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:995
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:19239
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6775
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:154
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3665
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:130
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2757
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:209
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:7103
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:340
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:75
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:963
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13975
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:674
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:69
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:807
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:160
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:66
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:485
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:997
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:746
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:882
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:248
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5992
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:18981
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:144
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:495
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:904
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:183
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:892
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:414
Bridge
@ Bridge
Definition: TrackUnit.h:65
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:976
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:280
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:366
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:426
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:525
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:65
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:134
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:124